﻿/*
* jQuery Mobile Framework Git Build: SHA1: e3c173e23dbbe30a11393528dc68244632f763f6 <> Date: Sat Jun 30 20:42:13 2012 +0200
* http://jquerymobile.com
*
* Copyright 2011-12 (c) The jQuery Foundation
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
*/


(function(root, doc, factory)
{
    if (typeof define === "function" && define.amd)
    {
        // AMD. Register as an anonymous module.
        define(["jquery"], function($)
        {
            factory($, root, doc);
            return $.mobile;
        });
    } else
    {
        // Browser globals
        factory(root.jQuery, root, doc);
    }
} (this, document, function(jQuery, window, document, undefined)
{
    (function($, window, undefined)
    {

        var nsNormalizeDict = {};

        // jQuery.mobile configurable options
        $.mobile = $.extend({}, {

            // Version of the jQuery Mobile Framework
            version: "1.2.0-pre",

            // Namespace used framework-wide for data-attrs. Default is no namespace
            ns: "",

            // Define the url parameter used for referencing widget-generated sub-pages.
            // Translates to to example.html&ui-page=subpageIdentifier
            // hash segment before &ui-page= is used to make Ajax request
            subPageUrlKey: "ui-page",

            // Class assigned to page currently in view, and during transitions
            activePageClass: "ui-page-active",

            // Class used for "active" button state, from CSS framework
            activeBtnClass: "ui-btn-active",

            // Class used for "focus" form element state, from CSS framework
            focusClass: "ui-focus",

            // Automatically handle clicks and form submissions through Ajax, when same-domain
            ajaxEnabled: true,

            // Automatically load and show pages based on location.hash
            hashListeningEnabled: true,

            // disable to prevent jquery from bothering with links
            linkBindingEnabled: true,

            // Set default page transition - 'none' for no transitions
            defaultPageTransition: "fade",

            // Set maximum window width for transitions to apply - 'false' for no limit
            maxTransitionWidth: false,

            // Minimum scroll distance that will be remembered when returning to a page
            minScrollBack: 250,

            // DEPRECATED: the following property is no longer in use, but defined until 2.0 to prevent conflicts
            touchOverflowEnabled: false,

            // Set default dialog transition - 'none' for no transitions
            defaultDialogTransition: "pop",

            // Error response message - appears when an Ajax page request fails
            pageLoadErrorMessage: "Error Loading Page",

            // For error messages, which theme does the box uses?
            pageLoadErrorMessageTheme: "e",

            //automatically initialize the DOM when it's ready
            autoInitializePage: true,

            pushStateEnabled: true,

            // allows users to opt in to ignoring content by marking a parent element as
            // data-ignored
            ignoreContentEnabled: false,

            // turn of binding to the native orientationchange due to android orientation behavior
            orientationChangeEnabled: true,

            buttonMarkup: {
                hoverDelay: 200
            },

            // TODO might be useful upstream in jquery itself ?
            keyCode: {
                ALT: 18,
                BACKSPACE: 8,
                CAPS_LOCK: 20,
                COMMA: 188,
                COMMAND: 91,
                COMMAND_LEFT: 91, // COMMAND
                COMMAND_RIGHT: 93,
                CONTROL: 17,
                DELETE: 46,
                DOWN: 40,
                END: 35,
                ENTER: 13,
                ESCAPE: 27,
                HOME: 36,
                INSERT: 45,
                LEFT: 37,
                MENU: 93, // COMMAND_RIGHT
                NUMPAD_ADD: 107,
                NUMPAD_DECIMAL: 110,
                NUMPAD_DIVIDE: 111,
                NUMPAD_ENTER: 108,
                NUMPAD_MULTIPLY: 106,
                NUMPAD_SUBTRACT: 109,
                PAGE_DOWN: 34,
                PAGE_UP: 33,
                PERIOD: 190,
                RIGHT: 39,
                SHIFT: 16,
                SPACE: 32,
                TAB: 9,
                UP: 38,
                WINDOWS: 91 // COMMAND
            },

            // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
            silentScroll: function(ypos)
            {
                if ($.type(ypos) !== "number")
                {
                    ypos = $.mobile.defaultHomeScroll;
                }

                // prevent scrollstart and scrollstop events
                $.event.special.scrollstart.enabled = false;

                setTimeout(function()
                {
                    window.scrollTo(0, ypos);
                    $(document).trigger("silentscroll", { x: 0, y: ypos });
                }, 20);

                setTimeout(function()
                {
                    $.event.special.scrollstart.enabled = true;
                }, 150);
            },

            // Expose our cache for testing purposes.
            nsNormalizeDict: nsNormalizeDict,

            // Take a data attribute property, prepend the namespace
            // and then camel case the attribute string. Add the result
            // to our nsNormalizeDict so we don't have to do this again.
            nsNormalize: function(prop)
            {
                if (!prop)
                {
                    return;
                }

                return nsNormalizeDict[prop] || (nsNormalizeDict[prop] = $.camelCase($.mobile.ns + prop));
            },

            // Find the closest parent with a theme class on it. Note that
            // we are not using $.fn.closest() on purpose here because this
            // method gets called quite a bit and we need it to be as fast
            // as possible.
            getInheritedTheme: function(el, defaultTheme)
            {
                var e = el[0],
				ltr = "",
				re = /ui-(bar|body|overlay)-([a-z])\b/,
				c, m;

                while (e)
                {
                    c = e.className || "";
                    if (c && (m = re.exec(c)) && (ltr = m[2]))
                    {
                        // We found a parent with a theme class
                        // on it so bail from this loop.
                        break;
                    }

                    e = e.parentNode;
                }

                // Return the theme letter we found, if none, return the
                // specified default.

                return ltr || defaultTheme || "a";
            },

            // TODO the following $ and $.fn extensions can/probably should be moved into jquery.mobile.core.helpers
            //
            // Find the closest javascript page element to gather settings data jsperf test
            // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit
            // possibly naive, but it shows that the parsing overhead for *just* the page selector vs
            // the page and dialog selector is negligable. This could probably be speed up by
            // doing a similar parent node traversal to the one found in the inherited theme code above
            closestPageData: function($target)
            {
                return $target
				.closest(':jqmData(role="page"), :jqmData(role="dialog")')
				.data("page");
            },

            enhanceable: function($set)
            {
                return this.haveParents($set, "enhance");
            },

            hijackable: function($set)
            {
                return this.haveParents($set, "ajax");
            },

            haveParents: function($set, attr)
            {
                if (!$.mobile.ignoreContentEnabled)
                {
                    return $set;
                }

                var count = $set.length,
				$newSet = $(),
				e, $element, excluded;

                for (var i = 0; i < count; i++)
                {
                    $element = $set.eq(i);
                    excluded = false;
                    e = $set[i];

                    while (e)
                    {
                        var c = e.getAttribute ? e.getAttribute("data-" + $.mobile.ns + attr) : "";

                        if (c === "false")
                        {
                            excluded = true;
                            break;
                        }

                        e = e.parentNode;
                    }

                    if (!excluded)
                    {
                        $newSet = $newSet.add($element);
                    }
                }

                return $newSet;
            },

            getScreenHeight: function()
            {
                // Native innerHeight returns more accurate value for this across platforms,
                // jQuery version is here as a normalized fallback for platforms like Symbian
                return window.innerHeight || $(window).height();
            }
        }, $.mobile);

        // Mobile version of data and removeData and hasData methods
        // ensures all data is set and retrieved using jQuery Mobile's data namespace
        $.fn.jqmData = function(prop, value)
        {
            var result;
            if (typeof prop !== "undefined")
            {
                if (prop)
                {
                    prop = $.mobile.nsNormalize(prop);
                }
                result = this.data.apply(this, arguments.length < 2 ? [prop] : [prop, value]);
            }
            return result;
        };

        $.jqmData = function(elem, prop, value)
        {
            var result;
            if (typeof prop !== "undefined")
            {
                result = $.data(elem, prop ? $.mobile.nsNormalize(prop) : prop, value);
            }
            return result;
        };

        $.fn.jqmRemoveData = function(prop)
        {
            return this.removeData($.mobile.nsNormalize(prop));
        };

        $.jqmRemoveData = function(elem, prop)
        {
            return $.removeData(elem, $.mobile.nsNormalize(prop));
        };

        $.fn.removeWithDependents = function()
        {
            $.removeWithDependents(this);
        };

        $.removeWithDependents = function(elem)
        {
            var $elem = $(elem);

            ($elem.jqmData('dependents') || $()).remove();
            $elem.remove();
        };

        $.fn.addDependents = function(newDependents)
        {
            $.addDependents($(this), newDependents);
        };

        $.addDependents = function(elem, newDependents)
        {
            var dependents = $(elem).jqmData('dependents') || $();

            $(elem).jqmData('dependents', $.merge(dependents, newDependents));
        };

        // note that this helper doesn't attempt to handle the callback
        // or setting of an html elements text, its only purpose is
        // to return the html encoded version of the text in all cases. (thus the name)
        $.fn.getEncodedText = function()
        {
            return $("<div/>").text($(this).text()).html();
        };

        // fluent helper function for the mobile namespaced equivalent
        $.fn.jqmEnhanceable = function()
        {
            return $.mobile.enhanceable(this);
        };

        $.fn.jqmHijackable = function()
        {
            return $.mobile.hijackable(this);
        };

        // Monkey-patching Sizzle to filter the :jqmData selector
        var oldFind = $.find,
		jqmDataRE = /:jqmData\(([^)]*)\)/g;

        $.find = function(selector, context, ret, extra)
        {
            selector = selector.replace(jqmDataRE, "[data-" + ($.mobile.ns || "") + "$1]");

            return oldFind.call(this, selector, context, ret, extra);
        };

        $.extend($.find, oldFind);

        $.find.matches = function(expr, set)
        {
            return $.find(expr, null, null, set);
        };

        $.find.matchesSelector = function(node, expr)
        {
            return $.find(expr, null, null, [node]).length > 0;
        };
    })(jQuery, this);


    /*!
    * jQuery UI Widget @VERSION
    *
    * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
    * Dual licensed under the MIT or GPL Version 2 licenses.
    * http://jquery.org/license
    *
    * http://docs.jquery.com/UI/Widget
    */
    (function($, undefined)
    {

        var slice = Array.prototype.slice;

        var _cleanData = $.cleanData;
        $.cleanData = function(elems)
        {
            for (var i = 0, elem; (elem = elems[i]) != null; i++)
            {
                try
                {
                    $(elem).triggerHandler("remove");
                    // http://bugs.jquery.com/ticket/8235
                } catch (e) { }
            }
            _cleanData(elems);
        };

        $.widget = function(name, base, prototype)
        {
            var fullName, existingConstructor, constructor, basePrototype,
		namespace = name.split(".")[0];

            name = name.split(".")[1];
            fullName = namespace + "-" + name;

            if (!prototype)
            {
                prototype = base;
                base = $.Widget;
            }

            // create selector for plugin
            $.expr[":"][fullName] = function(elem)
            {
                return !!$.data(elem, fullName);
            };

            $[namespace] = $[namespace] || {};
            existingConstructor = $[namespace][name];
            constructor = $[namespace][name] = function(options, element)
            {
                // allow instantiation without "new" keyword
                if (!this._createWidget)
                {
                    return new constructor(options, element);
                }

                // allow instantiation without initializing for simple inheritance
                // must use "new" keyword (the code above always passes args)
                if (arguments.length)
                {
                    this._createWidget(options, element);
                }
            };
            // extend with the existing constructor to carry over any static properties
            $.extend(constructor, existingConstructor, {
                version: prototype.version,
                // copy the object used to create the prototype in case we need to
                // redefine the widget later
                _proto: $.extend({}, prototype),
                // track widgets that inherit from this widget in case this widget is
                // redefined after a widget inherits from it
                _childConstructors: []
            });

            basePrototype = new base();
            // we need to make the options hash a property directly on the new instance
            // otherwise we'll modify the options hash on the prototype that we're
            // inheriting from
            basePrototype.options = $.widget.extend({}, basePrototype.options);
            $.each(prototype, function(prop, value)
            {
                if ($.isFunction(value))
                {
                    prototype[prop] = (function()
                    {
                        var _super = function()
                        {
                            return base.prototype[prop].apply(this, arguments);
                        };
                        var _superApply = function(args)
                        {
                            return base.prototype[prop].apply(this, args);
                        };
                        return function()
                        {
                            var __super = this._super,
						__superApply = this._superApply,
						returnValue;

                            this._super = _super;
                            this._superApply = _superApply;

                            returnValue = value.apply(this, arguments);

                            this._super = __super;
                            this._superApply = __superApply;

                            return returnValue;
                        };
                    })();
                }
            });
            constructor.prototype = $.widget.extend(basePrototype, {
                // TODO: remove support for widgetEventPrefix
                // always use the name + a colon as the prefix, e.g., draggable:start
                // don't prefix for widgets that aren't DOM-based
                widgetEventPrefix: name
            }, prototype, {
                constructor: constructor,
                namespace: namespace,
                widgetName: name,
                // TODO remove widgetBaseClass, see #8155
                widgetBaseClass: fullName,
                widgetFullName: fullName
            });

            // If this widget is being redefined then we need to find all widgets that
            // are inheriting from it and redefine all of them so that they inherit from
            // the new version of this widget. We're essentially trying to replace one
            // level in the prototype chain.
            if (existingConstructor)
            {
                $.each(existingConstructor._childConstructors, function(i, child)
                {
                    var childPrototype = child.prototype;

                    // redefine the child widget using the same prototype that was
                    // originally used, but inherit from the new version of the base
                    $.widget(childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto);
                });
                // remove the list of existing child constructors from the old constructor
                // so the old child constructors can be garbage collected
                delete existingConstructor._childConstructors;
            } else
            {
                base._childConstructors.push(constructor);
            }

            $.widget.bridge(name, constructor);
        };

        $.widget.extend = function(target)
        {
            var input = slice.call(arguments, 1),
		inputIndex = 0,
		inputLength = input.length,
		key,
		value;
            for (; inputIndex < inputLength; inputIndex++)
            {
                for (key in input[inputIndex])
                {
                    value = input[inputIndex][key];
                    if (input[inputIndex].hasOwnProperty(key) && value !== undefined)
                    {
                        target[key] = $.isPlainObject(value) ? $.widget.extend({}, target[key], value) : value;
                    }
                }
            }
            return target;
        };

        $.widget.bridge = function(name, object)
        {
            var fullName = object.prototype.widgetFullName;
            $.fn[name] = function(options)
            {
                var isMethodCall = typeof options === "string",
			args = slice.call(arguments, 1),
			returnValue = this;

                // allow multiple hashes to be passed on init
                options = !isMethodCall && args.length ?
			$.widget.extend.apply(null, [options].concat(args)) :
			options;

                if (isMethodCall)
                {
                    this.each(function()
                    {
                        var instance = $.data(this, fullName);
                        if (!instance)
                        {
                            return $.error("cannot call methods on " + name + " prior to initialization; " +
						"attempted to call method '" + options + "'");
                        }
                        if (!$.isFunction(instance[options]) || options.charAt(0) === "_")
                        {
                            return $.error("no such method '" + options + "' for " + name + " widget instance");
                        }
                        var methodValue = instance[options].apply(instance, args);
                        if (methodValue !== instance && methodValue !== undefined)
                        {
                            returnValue = methodValue && methodValue.jquery ?
						returnValue.pushStack(methodValue.get()) :
						methodValue;
                            return false;
                        }
                    });
                } else
                {
                    this.each(function()
                    {
                        var instance = $.data(this, fullName);
                        if (instance)
                        {
                            instance.option(options || {})._init();
                        } else
                        {
                            new object(options, this);
                        }
                    });
                }

                return returnValue;
            };
        };

        $.Widget = function(options, element) { };
        $.Widget._childConstructors = [];

        $.Widget.prototype = {
            widgetName: "widget",
            widgetEventPrefix: "",
            defaultElement: "<div>",
            options: {
                disabled: false,

                // callbacks
                create: null
            },
            _createWidget: function(options, element)
            {
                element = $(element || this.defaultElement || this)[0];
                this.element = $(element);
                this.options = $.widget.extend({},
			this.options,
			this._getCreateOptions(),
			options);

                this.bindings = $();
                this.hoverable = $();
                this.focusable = $();

                if (element !== this)
                {
                    // 1.9 BC for #7810
                    // TODO remove dual storage
                    $.data(element, this.widgetName, this);
                    $.data(element, this.widgetFullName, this);
                    this._bind({ remove: "destroy" });
                    this.document = $(element.style ?
                    // element within the document
				element.ownerDocument :
                    // element is window or document
				element.document || element);
                    this.window = $(this.document[0].defaultView || this.document[0].parentWindow);
                }

                this._create();
                this._trigger("create", null, this._getCreateEventData());
                this._init();
            },
            _getCreateOptions: $.noop,
            _getCreateEventData: $.noop,
            _create: $.noop,
            _init: $.noop,

            destroy: function()
            {
                this._destroy();
                // we can probably remove the unbind calls in 2.0
                // all event bindings should go through this._bind()
                this.element
			.unbind("." + this.widgetName)
                // 1.9 BC for #7810
                // TODO remove dual storage
			.removeData(this.widgetName)
			.removeData(this.widgetFullName);
                this.widget()
			.unbind("." + this.widgetName)
			.removeAttr("aria-disabled")
			.removeClass(
				this.widgetFullName + "-disabled " +
				"ui-state-disabled");

                // clean up events and states
                this.bindings.unbind("." + this.widgetName);
                this.hoverable.removeClass("ui-state-hover");
                this.focusable.removeClass("ui-state-focus");
            },
            _destroy: $.noop,

            widget: function()
            {
                return this.element;
            },

            option: function(key, value)
            {
                var options = key,
			parts,
			curOption,
			i;

                if (arguments.length === 0)
                {
                    // don't return a reference to the internal hash
                    return $.widget.extend({}, this.options);
                }

                if (typeof key === "string")
                {
                    // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
                    options = {};
                    parts = key.split(".");
                    key = parts.shift();
                    if (parts.length)
                    {
                        curOption = options[key] = $.widget.extend({}, this.options[key]);
                        for (i = 0; i < parts.length - 1; i++)
                        {
                            curOption[parts[i]] = curOption[parts[i]] || {};
                            curOption = curOption[parts[i]];
                        }
                        key = parts.pop();
                        if (value === undefined)
                        {
                            return curOption[key] === undefined ? null : curOption[key];
                        }
                        curOption[key] = value;
                    } else
                    {
                        if (value === undefined)
                        {
                            return this.options[key] === undefined ? null : this.options[key];
                        }
                        options[key] = value;
                    }
                }

                this._setOptions(options);

                return this;
            },
            _setOptions: function(options)
            {
                var key;

                for (key in options)
                {
                    this._setOption(key, options[key]);
                }

                return this;
            },
            _setOption: function(key, value)
            {
                this.options[key] = value;

                if (key === "disabled")
                {
                    this.widget()
				.toggleClass(this.widgetFullName + "-disabled ui-state-disabled", !!value)
				.attr("aria-disabled", value);
                    this.hoverable.removeClass("ui-state-hover");
                    this.focusable.removeClass("ui-state-focus");
                }

                return this;
            },

            enable: function()
            {
                return this._setOption("disabled", false);
            },
            disable: function()
            {
                return this._setOption("disabled", true);
            },

            _bind: function(element, handlers)
            {
                // no element argument, shuffle and use this.element
                if (!handlers)
                {
                    handlers = element;
                    element = this.element;
                } else
                {
                    // accept selectors, DOM elements
                    element = $(element);
                    this.bindings = this.bindings.add(element);
                }

                var instance = this;
                $.each(handlers, function(event, handler)
                {
                    function handlerProxy()
                    {
                        // allow widgets to customize the disabled handling
                        // - disabled as an array instead of boolean
                        // - disabled class as method for disabling individual parts
                        if (instance.options.disabled === true ||
						$(this).hasClass("ui-state-disabled"))
                        {
                            return;
                        }
                        return (typeof handler === "string" ? instance[handler] : handler)
					.apply(instance, arguments);
                    }

                    // copy the guid so direct unbinding works
                    if (typeof handler !== "string")
                    {
                        handlerProxy.guid = handler.guid =
					handler.guid || handlerProxy.guid || jQuery.guid++;
                    }

                    var match = event.match(/^(\w+)\s*(.*)$/),
				eventName = match[1] + "." + instance.widgetName,
				selector = match[2];
                    if (selector)
                    {
                        instance.widget().delegate(selector, eventName, handlerProxy);
                    } else
                    {
                        element.bind(eventName, handlerProxy);
                    }
                });
            },

            _delay: function(handler, delay)
            {
                function handlerProxy()
                {
                    return (typeof handler === "string" ? instance[handler] : handler)
				.apply(instance, arguments);
                }
                var instance = this;
                return setTimeout(handlerProxy, delay || 0);
            },

            _hoverable: function(element)
            {
                this.hoverable = this.hoverable.add(element);
                this._bind(element, {
                    mouseenter: function(event)
                    {
                        $(event.currentTarget).addClass("ui-state-hover");
                    },
                    mouseleave: function(event)
                    {
                        $(event.currentTarget).removeClass("ui-state-hover");
                    }
                });
            },

            _focusable: function(element)
            {
                this.focusable = this.focusable.add(element);
                this._bind(element, {
                    focusin: function(event)
                    {
                        $(event.currentTarget).addClass("ui-state-focus");
                    },
                    focusout: function(event)
                    {
                        $(event.currentTarget).removeClass("ui-state-focus");
                    }
                });
            },

            _trigger: function(type, event, data)
            {
                var prop, orig,
			callback = this.options[type];

                data = data || {};
                event = $.Event(event);
                event.type = (type === this.widgetEventPrefix ?
			type :
			this.widgetEventPrefix + type).toLowerCase();
                // the original event may come from any element
                // so we need to reset the target on the new event
                event.target = this.element[0];

                // copy original event properties over to the new event
                orig = event.originalEvent;
                if (orig)
                {
                    for (prop in orig)
                    {
                        if (!(prop in event))
                        {
                            event[prop] = orig[prop];
                        }
                    }
                }

                this.element.trigger(event, data);
                return !($.isFunction(callback) &&
			callback.apply(this.element[0], [event].concat(data)) === false ||
			event.isDefaultPrevented());
            }
        };

        $.each({ show: "fadeIn", hide: "fadeOut" }, function(method, defaultEffect)
        {
            $.Widget.prototype["_" + method] = function(element, options, callback)
            {
                if (typeof options === "string")
                {
                    options = { effect: options };
                }
                var hasOptions,
			effectName = !options ?
				method :
				options === true || typeof options === "number" ?
					defaultEffect :
					options.effect || defaultEffect;
                options = options || {};
                if (typeof options === "number")
                {
                    options = { duration: options };
                }
                hasOptions = !$.isEmptyObject(options);
                options.complete = callback;
                if (options.delay)
                {
                    element.delay(options.delay);
                }
                if (hasOptions && $.effects && ($.effects.effect[effectName] || $.uiBackCompat !== false && $.effects[effectName]))
                {
                    element[method](options);
                } else if (effectName !== method && element[effectName])
                {
                    element[effectName](options.duration, options.easing, callback);
                } else
                {
                    element.queue(function(next)
                    {
                        $(this)[method]();
                        if (callback)
                        {
                            callback.call(element[0]);
                        }
                        next();
                    });
                }
            };
        });

        // DEPRECATED
        if ($.uiBackCompat !== false)
        {
            $.Widget.prototype._getCreateOptions = function()
            {
                return $.metadata && $.metadata.get(this.element[0])[this.widgetName];
            };
        }

    })(jQuery);

    (function($, undefined)
    {

        $.widget("mobile.widget", {
            // decorate the parent _createWidget to trigger `widgetinit` for users
            // who wish to do post post `widgetcreate` alterations/additions
            //
            // TODO create a pull request for jquery ui to trigger this event
            // in the original _createWidget
            _createWidget: function()
            {
                $.Widget.prototype._createWidget.apply(this, arguments);
                this._trigger('init');
            },

            _getCreateOptions: function()
            {

                var elem = this.element,
			options = {};

                $.each(this.options, function(option)
                {

                    var value = elem.jqmData(option.replace(/[A-Z]/g, function(c)
                    {
                        return "-" + c.toLowerCase();
                    })
					);

                    if (value !== undefined)
                    {
                        options[option] = value;
                    }
                });

                return options;
            },

            enhanceWithin: function(target, useKeepNative)
            {
                this.enhance($(this.options.initSelector, $(target)), useKeepNative);
            },

            enhance: function(targets, useKeepNative)
            {
                var page, keepNative, $widgetElements = $(targets), self = this;

                // if ignoreContentEnabled is set to true the framework should
                // only enhance the selected elements when they do NOT have a
                // parent with the data-namespace-ignore attribute
                $widgetElements = $.mobile.enhanceable($widgetElements);

                if (useKeepNative && $widgetElements.length)
                {
                    // TODO remove dependency on the page widget for the keepNative.
                    // Currently the keepNative value is defined on the page prototype so
                    // the method is as well
                    page = $.mobile.closestPageData($widgetElements);
                    keepNative = (page && page.keepNativeSelector()) || "";

                    $widgetElements = $widgetElements.not(keepNative);
                }

                $widgetElements[this.widgetName]();
            },

            raise: function(msg)
            {
                throw "Widget [" + this.widgetName + "]: " + msg;
            }
        });

    })(jQuery);


    (function($, window)
    {
        // DEPRECATED
        // NOTE global mobile object settings
        $.extend($.mobile, {
            // DEPRECATED Should the text be visble in the loading message?
            loadingMessageTextVisible: undefined,

            // DEPRECATED When the text is visible, what theme does the loading box use?
            loadingMessageTheme: undefined,

            // DEPRECATED default message setting
            loadingMessage: undefined,

            // DEPRECATED
            // Turn on/off page loading message. Theme doubles as an object argument
            // with the following shape: { theme: '', text: '', html: '', textVisible: '' }
            // NOTE that the $.mobile.loading* settings and params past the first are deprecated
            showPageLoadingMsg: function(theme, msgText, textonly)
            {
                $.mobile.loading('show', theme, msgText, textonly);
            },

            // DEPRECATED
            hidePageLoadingMsg: function()
            {
                $.mobile.loading('hide');
            },

            loading: function()
            {
                this.loaderWidget.loader.apply(this.loaderWidget, arguments);
            }
        });

        // TODO move loader class down into the widget settings
        var loaderClass = "ui-loader", $html = $("html"), $window = $(window);

        $.widget("mobile.loader", {
            // NOTE if the global config settings are defined they will override these
            //      options
            options: {
                // the theme for the loading message
                theme: "a",

                // whether the text in the loading message is shown
                textVisible: false,

                // custom html for the inner content of the loading message
                html: "",

                // the text to be displayed when the popup is shown
                text: "loading"
            },

            defaultHtml: "<div class='" + loaderClass + "'>" +
			"<span class='ui-icon ui-icon-loading'></span>" +
			"<h1></h1>" +
			"</div>",

            // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
            fakeFixLoader: function()
            {
                var activeBtn = $("." + $.mobile.activeBtnClass).first();

                this.element
				.css({
				    top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
						activeBtn.length && activeBtn.offset().top || 100
				});
            },

            // check position of loader to see if it appears to be "fixed" to center
            // if not, use abs positioning
            checkLoaderPosition: function()
            {
                var offset = this.element.offset(),
				scrollTop = $window.scrollTop(),
				screenHeight = $.mobile.getScreenHeight();

                if (offset.top < scrollTop || (offset.top - scrollTop) > screenHeight)
                {
                    this.element.addClass("ui-loader-fakefix");
                    this.fakeFixLoader();
                    $window
					.unbind("scroll", this.checkLoaderPosition)
					.bind("scroll", this.fakeFixLoader);
                }
            },

            resetHtml: function()
            {
                this.element.html($(this.defaultHtml).html());
            },

            // Turn on/off page loading message. Theme doubles as an object argument
            // with the following shape: { theme: '', text: '', html: '', textVisible: '' }
            // NOTE that the $.mobile.loading* settings and params past the first are deprecated
            // TODO sweet jesus we need to break some of this out
            show: function(theme, msgText, textonly)
            {
                var textVisible, message, $header, loadSettings;

                this.resetHtml();

                // use the prototype options so that people can set them globally at
                // mobile init. Consistency, it's what's for dinner
                if ($.type(theme) === "object")
                {
                    loadSettings = $.extend({}, this.options, theme);

                    // prefer object property from the param then the old theme setting
                    theme = loadSettings.theme || $.mobile.loadingMessageTheme;
                } else
                {
                    loadSettings = this.options;

                    // here we prefer the them value passed as a string argument, then
                    // we prefer the global option because we can't use undefined default
                    // prototype options, then the prototype option
                    theme = theme || $.mobile.loadingMessageTheme || loadSettings.theme;
                }

                // set the message text, prefer the param, then the settings object
                // then loading message
                message = msgText || $.mobile.loadingMessage || loadSettings.text;

                // prepare the dom
                $html.addClass("ui-loading");

                if ($.mobile.loadingMessage !== false || loadSettings.html)
                {
                    // boolean values require a bit more work :P, supports object properties
                    // and old settings
                    if ($.mobile.loadingMessageTextVisible !== undefined)
                    {
                        textVisible = $.mobile.loadingMessageTextVisible;
                    } else
                    {
                        textVisible = loadSettings.textVisible;
                    }

                    // add the proper css given the options (theme, text, etc)
                    // Force text visibility if the second argument was supplied, or
                    // if the text was explicitly set in the object args
                    this.element.attr("class", loaderClass +
					" ui-corner-all ui-body-" + theme +
					" ui-loader-" + (textVisible || msgText || theme.text ? "verbose" : "default") +
					(loadSettings.textonly || textonly ? " ui-loader-textonly" : ""));

                    // TODO verify that jquery.fn.html is ok to use in both cases here
                    //      this might be overly defensive in preventing unknowing xss
                    // if the html attribute is defined on the loading settings, use that
                    // otherwise use the fallbacks from above
                    if (loadSettings.html)
                    {
                        this.element.html(loadSettings.html);
                    } else
                    {
                        this.element.find("h1").text(message);
                    }

                    // attach the loader to the DOM
                    this.element.appendTo($.mobile.pageContainer);

                    // check that the loader is visible
                    this.checkLoaderPosition();

                    // on scroll check the loader position
                    $window.bind("scroll", $.proxy(this.checkLoaderPosition, this));
                }
            },

            hide: function()
            {
                $html.removeClass("ui-loading");

                if ($.mobile.loadingMessage)
                {
                    this.element.removeClass("ui-loader-fakefix");
                }

                $(window).unbind("scroll", $.proxy(this.fakeFixLoader, this));
                $(window).unbind("scroll", $.proxy(this.checkLoaderPosition, this));
            }
        });

        $window.bind('pagecontainercreate', function()
        {
            $.mobile.loaderWidget = $.mobile.loaderWidget || $($.mobile.loader.prototype.defaultHtml).loader();
        });
    })(jQuery, this);



    // This plugin is an experiment for abstracting away the touch and mouse
    // events so that developers don't have to worry about which method of input
    // the device their document is loaded on supports.
    //
    // The idea here is to allow the developer to register listeners for the
    // basic mouse events, such as mousedown, mousemove, mouseup, and click,
    // and the plugin will take care of registering the correct listeners
    // behind the scenes to invoke the listener at the fastest possible time
    // for that device, while still retaining the order of event firing in
    // the traditional mouse environment, should multiple handlers be registered
    // on the same element for different events.
    //
    // The current version exposes the following virtual events to jQuery bind methods:
    // "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"

    (function($, window, document, undefined)
    {

        var dataPropertyName = "virtualMouseBindings",
	touchTargetPropertyName = "virtualTouchID",
	virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),
	touchEventProps = "clientX clientY pageX pageY screenX screenY".split(" "),
	mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
	mouseEventProps = $.event.props.concat(mouseHookProps),
	activeDocHandlers = {},
	resetTimerID = 0,
	startX = 0,
	startY = 0,
	didScroll = false,
	clickBlockList = [],
	blockMouseTriggers = false,
	blockTouchTriggers = false,
	eventCaptureSupported = "addEventListener" in document,
	$document = $(document),
	nextTouchID = 1,
	lastTouchID = 0, threshold;

        $.vmouse = {
            moveDistanceThreshold: 10,
            clickDistanceThreshold: 10,
            resetTimerDuration: 1500
        };

        function getNativeEvent(event)
        {

            while (event && typeof event.originalEvent !== "undefined")
            {
                event = event.originalEvent;
            }
            return event;
        }

        function createVirtualEvent(event, eventType)
        {

            var t = event.type,
		oe, props, ne, prop, ct, touch, i, j, len;

            event = $.Event(event);
            event.type = eventType;

            oe = event.originalEvent;
            props = $.event.props;

            // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
            // https://github.com/jquery/jquery-mobile/issues/3280
            if (t.search(/^(mouse|click)/) > -1)
            {
                props = mouseEventProps;
            }

            // copy original event properties over to the new event
            // this would happen if we could call $.event.fix instead of $.Event
            // but we don't have a way to force an event to be fixed multiple times
            if (oe)
            {
                for (i = props.length, prop; i; )
                {
                    prop = props[--i];
                    event[prop] = oe[prop];
                }
            }

            // make sure that if the mouse and click virtual events are generated
            // without a .which one is defined
            if (t.search(/mouse(down|up)|click/) > -1 && !event.which)
            {
                event.which = 1;
            }

            if (t.search(/^touch/) !== -1)
            {
                ne = getNativeEvent(oe);
                t = ne.touches;
                ct = ne.changedTouches;
                touch = (t && t.length) ? t[0] : ((ct && ct.length) ? ct[0] : undefined);

                if (touch)
                {
                    for (j = 0, len = touchEventProps.length; j < len; j++)
                    {
                        prop = touchEventProps[j];
                        event[prop] = touch[prop];
                    }
                }
            }

            return event;
        }

        function getVirtualBindingFlags(element)
        {

            var flags = {},
		b, k;

            while (element)
            {

                b = $.data(element, dataPropertyName);

                for (k in b)
                {
                    if (b[k])
                    {
                        flags[k] = flags.hasVirtualBinding = true;
                    }
                }
                element = element.parentNode;
            }
            return flags;
        }

        function getClosestElementWithVirtualBinding(element, eventType)
        {
            var b;
            while (element)
            {

                b = $.data(element, dataPropertyName);

                if (b && (!eventType || b[eventType]))
                {
                    return element;
                }
                element = element.parentNode;
            }
            return null;
        }

        function enableTouchBindings()
        {
            blockTouchTriggers = false;
        }

        function disableTouchBindings()
        {
            blockTouchTriggers = true;
        }

        function enableMouseBindings()
        {
            lastTouchID = 0;
            clickBlockList.length = 0;
            blockMouseTriggers = false;

            // When mouse bindings are enabled, our
            // touch bindings are disabled.
            disableTouchBindings();
        }

        function disableMouseBindings()
        {
            // When mouse bindings are disabled, our
            // touch bindings are enabled.
            enableTouchBindings();
        }

        function startResetTimer()
        {
            clearResetTimer();
            resetTimerID = setTimeout(function()
            {
                resetTimerID = 0;
                enableMouseBindings();
            }, $.vmouse.resetTimerDuration);
        }

        function clearResetTimer()
        {
            if (resetTimerID)
            {
                clearTimeout(resetTimerID);
                resetTimerID = 0;
            }
        }

        function triggerVirtualEvent(eventType, event, flags)
        {
            var ve;

            if ((flags && flags[eventType]) ||
				(!flags && getClosestElementWithVirtualBinding(event.target, eventType)))
            {

                ve = createVirtualEvent(event, eventType);

                $(event.target).trigger(ve);
            }

            return ve;
        }

        function mouseEventCallback(event)
        {
            var touchID = $.data(event.target, touchTargetPropertyName);

            if (!blockMouseTriggers && (!lastTouchID || lastTouchID !== touchID))
            {
                var ve = triggerVirtualEvent("v" + event.type, event);
                if (ve)
                {
                    if (ve.isDefaultPrevented())
                    {
                        event.preventDefault();
                    }
                    if (ve.isPropagationStopped())
                    {
                        event.stopPropagation();
                    }
                    if (ve.isImmediatePropagationStopped())
                    {
                        event.stopImmediatePropagation();
                    }
                }
            }
        }

        function handleTouchStart(event)
        {

            var touches = getNativeEvent(event).touches,
		target, flags;

            if (touches && touches.length === 1)
            {

                target = event.target;
                flags = getVirtualBindingFlags(target);

                if (flags.hasVirtualBinding)
                {

                    lastTouchID = nextTouchID++;
                    $.data(target, touchTargetPropertyName, lastTouchID);

                    clearResetTimer();

                    disableMouseBindings();
                    didScroll = false;

                    var t = getNativeEvent(event).touches[0];
                    startX = t.pageX;
                    startY = t.pageY;

                    triggerVirtualEvent("vmouseover", event, flags);
                    triggerVirtualEvent("vmousedown", event, flags);
                }
            }
        }

        function handleScroll(event)
        {
            if (blockTouchTriggers)
            {
                return;
            }

            if (!didScroll)
            {
                triggerVirtualEvent("vmousecancel", event, getVirtualBindingFlags(event.target));
            }

            didScroll = true;
            startResetTimer();
        }

        function handleTouchMove(event)
        {
            if (blockTouchTriggers)
            {
                return;
            }

            var t = getNativeEvent(event).touches[0],
		didCancel = didScroll,
		moveThreshold = $.vmouse.moveDistanceThreshold,
		flags = getVirtualBindingFlags(event.target);

            didScroll = didScroll ||
			(Math.abs(t.pageX - startX) > moveThreshold ||
				Math.abs(t.pageY - startY) > moveThreshold);


            if (didScroll && !didCancel)
            {
                triggerVirtualEvent("vmousecancel", event, flags);
            }

            triggerVirtualEvent("vmousemove", event, flags);
            startResetTimer();
        }

        function handleTouchEnd(event)
        {
            if (blockTouchTriggers)
            {
                return;
            }

            disableTouchBindings();

            var flags = getVirtualBindingFlags(event.target),
		t;
            triggerVirtualEvent("vmouseup", event, flags);

            if (!didScroll)
            {
                var ve = triggerVirtualEvent("vclick", event, flags);
                if (ve && ve.isDefaultPrevented())
                {
                    // The target of the mouse events that follow the touchend
                    // event don't necessarily match the target used during the
                    // touch. This means we need to rely on coordinates for blocking
                    // any click that is generated.
                    t = getNativeEvent(event).changedTouches[0];
                    clickBlockList.push({
                        touchID: lastTouchID,
                        x: t.clientX,
                        y: t.clientY
                    });

                    // Prevent any mouse events that follow from triggering
                    // virtual event notifications.
                    blockMouseTriggers = true;
                }
            }
            triggerVirtualEvent("vmouseout", event, flags);
            didScroll = false;

            startResetTimer();
        }

        function hasVirtualBindings(ele)
        {
            var bindings = $.data(ele, dataPropertyName),
		k;

            if (bindings)
            {
                for (k in bindings)
                {
                    if (bindings[k])
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        function dummyMouseHandler() { }

        function getSpecialEventObject(eventType)
        {
            var realType = eventType.substr(1);

            return {
                setup: function(data, namespace)
                {
                    // If this is the first virtual mouse binding for this element,
                    // add a bindings object to its data.

                    if (!hasVirtualBindings(this))
                    {
                        $.data(this, dataPropertyName, {});
                    }

                    // If setup is called, we know it is the first binding for this
                    // eventType, so initialize the count for the eventType to zero.
                    var bindings = $.data(this, dataPropertyName);
                    bindings[eventType] = true;

                    // If this is the first virtual mouse event for this type,
                    // register a global handler on the document.

                    activeDocHandlers[eventType] = (activeDocHandlers[eventType] || 0) + 1;

                    if (activeDocHandlers[eventType] === 1)
                    {
                        $document.bind(realType, mouseEventCallback);
                    }

                    // Some browsers, like Opera Mini, won't dispatch mouse/click events
                    // for elements unless they actually have handlers registered on them.
                    // To get around this, we register dummy handlers on the elements.

                    $(this).bind(realType, dummyMouseHandler);

                    // For now, if event capture is not supported, we rely on mouse handlers.
                    if (eventCaptureSupported)
                    {
                        // If this is the first virtual mouse binding for the document,
                        // register our touchstart handler on the document.

                        activeDocHandlers["touchstart"] = (activeDocHandlers["touchstart"] || 0) + 1;

                        if (activeDocHandlers["touchstart"] === 1)
                        {
                            $document.bind("touchstart", handleTouchStart)
						.bind("touchend", handleTouchEnd)

                            // On touch platforms, touching the screen and then dragging your finger
                            // causes the window content to scroll after some distance threshold is
                            // exceeded. On these platforms, a scroll prevents a click event from being
                            // dispatched, and on some platforms, even the touchend is suppressed. To
                            // mimic the suppression of the click event, we need to watch for a scroll
                            // event. Unfortunately, some platforms like iOS don't dispatch scroll
                            // events until *AFTER* the user lifts their finger (touchend). This means
                            // we need to watch both scroll and touchmove events to figure out whether
                            // or not a scroll happenens before the touchend event is fired.

						.bind("touchmove", handleTouchMove)
						.bind("scroll", handleScroll);
                        }
                    }
                },

                teardown: function(data, namespace)
                {
                    // If this is the last virtual binding for this eventType,
                    // remove its global handler from the document.

                    --activeDocHandlers[eventType];

                    if (!activeDocHandlers[eventType])
                    {
                        $document.unbind(realType, mouseEventCallback);
                    }

                    if (eventCaptureSupported)
                    {
                        // If this is the last virtual mouse binding in existence,
                        // remove our document touchstart listener.

                        --activeDocHandlers["touchstart"];

                        if (!activeDocHandlers["touchstart"])
                        {
                            $document.unbind("touchstart", handleTouchStart)
						.unbind("touchmove", handleTouchMove)
						.unbind("touchend", handleTouchEnd)
						.unbind("scroll", handleScroll);
                        }
                    }

                    var $this = $(this),
				bindings = $.data(this, dataPropertyName);

                    // teardown may be called when an element was
                    // removed from the DOM. If this is the case,
                    // jQuery core may have already stripped the element
                    // of any data bindings so we need to check it before
                    // using it.
                    if (bindings)
                    {
                        bindings[eventType] = false;
                    }

                    // Unregister the dummy event handler.

                    $this.unbind(realType, dummyMouseHandler);

                    // If this is the last virtual mouse binding on the
                    // element, remove the binding data from the element.

                    if (!hasVirtualBindings(this))
                    {
                        $this.removeData(dataPropertyName);
                    }
                }
            };
        }

        // Expose our custom events to the jQuery bind/unbind mechanism.

        for (var i = 0; i < virtualEventNames.length; i++)
        {
            $.event.special[virtualEventNames[i]] = getSpecialEventObject(virtualEventNames[i]);
        }

        // Add a capture click handler to block clicks.
        // Note that we require event capture support for this so if the device
        // doesn't support it, we punt for now and rely solely on mouse events.
        if (eventCaptureSupported)
        {
            document.addEventListener("click", function(e)
            {
                var cnt = clickBlockList.length,
			target = e.target,
			x, y, ele, i, o, touchID;

                if (cnt)
                {
                    x = e.clientX;
                    y = e.clientY;
                    threshold = $.vmouse.clickDistanceThreshold;

                    // The idea here is to run through the clickBlockList to see if
                    // the current click event is in the proximity of one of our
                    // vclick events that had preventDefault() called on it. If we find
                    // one, then we block the click.
                    //
                    // Why do we have to rely on proximity?
                    //
                    // Because the target of the touch event that triggered the vclick
                    // can be different from the target of the click event synthesized
                    // by the browser. The target of a mouse/click event that is syntehsized
                    // from a touch event seems to be implementation specific. For example,
                    // some browsers will fire mouse/click events for a link that is near
                    // a touch event, even though the target of the touchstart/touchend event
                    // says the user touched outside the link. Also, it seems that with most
                    // browsers, the target of the mouse/click event is not calculated until the
                    // time it is dispatched, so if you replace an element that you touched
                    // with another element, the target of the mouse/click will be the new
                    // element underneath that point.
                    //
                    // Aside from proximity, we also check to see if the target and any
                    // of its ancestors were the ones that blocked a click. This is necessary
                    // because of the strange mouse/click target calculation done in the
                    // Android 2.1 browser, where if you click on an element, and there is a
                    // mouse/click handler on one of its ancestors, the target will be the
                    // innermost child of the touched element, even if that child is no where
                    // near the point of touch.

                    ele = target;

                    while (ele)
                    {
                        for (i = 0; i < cnt; i++)
                        {
                            o = clickBlockList[i];
                            touchID = 0;

                            if ((ele === target && Math.abs(o.x - x) < threshold && Math.abs(o.y - y) < threshold) ||
								$.data(ele, touchTargetPropertyName) === o.touchID)
                            {
                                // XXX: We may want to consider removing matches from the block list
                                //      instead of waiting for the reset timer to fire.
                                e.preventDefault();
                                e.stopPropagation();
                                return;
                            }
                        }
                        ele = ele.parentNode;
                    }
                }
            }, true);
        }
    })(jQuery, window, document);


    (function($, window, undefined)
    {
        // add new event shortcuts
        $.each(("touchstart touchmove touchend " +
		"tap taphold " +
		"swipe swipeleft swiperight " +
		"scrollstart scrollstop").split(" "), function(i, name)
		{

		    $.fn[name] = function(fn)
		    {
		        return fn ? this.bind(name, fn) : this.trigger(name);
		    };

		    $.attrFn[name] = true;
		});

        var supportTouch = "ontouchend" in document,
		scrollEvent = "touchmove scroll",
		touchStartEvent = supportTouch ? "touchstart" : "mousedown",
		touchStopEvent = supportTouch ? "touchend" : "mouseup",
		touchMoveEvent = supportTouch ? "touchmove" : "mousemove";

        function triggerCustomEvent(obj, eventType, event)
        {
            var originalType = event.type;
            event.type = eventType;
            $.event.handle.call(obj, event);
            event.type = originalType;
        }

        // also handles scrollstop
        $.event.special.scrollstart = {

            enabled: true,

            setup: function()
            {

                var thisObject = this,
				$this = $(thisObject),
				scrolling,
				timer;

                function trigger(event, state)
                {
                    scrolling = state;
                    triggerCustomEvent(thisObject, scrolling ? "scrollstart" : "scrollstop", event);
                }

                // iPhone triggers scroll after a small delay; use touchmove instead
                $this.bind(scrollEvent, function(event)
                {

                    if (!$.event.special.scrollstart.enabled)
                    {
                        return;
                    }

                    if (!scrolling)
                    {
                        trigger(event, true);
                    }

                    clearTimeout(timer);
                    timer = setTimeout(function()
                    {
                        trigger(event, false);
                    }, 50);
                });
            }
        };

        // also handles taphold
        $.event.special.tap = {
            tapholdThreshold: 750,

            setup: function()
            {
                var thisObject = this,
				$this = $(thisObject);

                $this.bind("vmousedown", function(event)
                {

                    if (event.which && event.which !== 1)
                    {
                        return false;
                    }

                    var origTarget = event.target,
					origEvent = event.originalEvent,
					timer;

                    function clearTapTimer()
                    {
                        clearTimeout(timer);
                    }

                    function clearTapHandlers()
                    {
                        clearTapTimer();

                        $this.unbind("vclick", clickHandler)
						.unbind("vmouseup", clearTapTimer);
                        $(document).unbind("vmousecancel", clearTapHandlers);
                    }

                    function clickHandler(event)
                    {
                        clearTapHandlers();

                        // ONLY trigger a 'tap' event if the start target is
                        // the same as the stop target.
                        if (origTarget === event.target)
                        {
                            triggerCustomEvent(thisObject, "tap", event);
                        }
                    }

                    $this.bind("vmouseup", clearTapTimer)
					.bind("vclick", clickHandler);
                    $(document).bind("vmousecancel", clearTapHandlers);

                    timer = setTimeout(function()
                    {
                        triggerCustomEvent(thisObject, "taphold", $.Event("taphold", { target: origTarget }));
                    }, $.event.special.tap.tapholdThreshold);
                });
            }
        };

        // also handles swipeleft, swiperight
        $.event.special.swipe = {
            scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.

            durationThreshold: 1000, // More time than this, and it isn't a swipe.

            horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.

            verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.

            setup: function()
            {
                var thisObject = this,
				$this = $(thisObject);

                $this.bind(touchStartEvent, function(event)
                {
                    var data = event.originalEvent.touches ?
						event.originalEvent.touches[0] : event,
					start = {
					    time: (new Date()).getTime(),
					    coords: [data.pageX, data.pageY],
					    origin: $(event.target)
					},
					stop;

                    function moveHandler(event)
                    {

                        if (!start)
                        {
                            return;
                        }

                        var data = event.originalEvent.touches ?
						event.originalEvent.touches[0] : event;

                        stop = {
                            time: (new Date()).getTime(),
                            coords: [data.pageX, data.pageY]
                        };

                        // prevent scrolling
                        if (Math.abs(start.coords[0] - stop.coords[0]) > $.event.special.swipe.scrollSupressionThreshold)
                        {
                            event.preventDefault();
                        }
                    }

                    $this.bind(touchMoveEvent, moveHandler)
					.one(touchStopEvent, function(event)
					{
					    $this.unbind(touchMoveEvent, moveHandler);

					    if (start && stop)
					    {
					        if (stop.time - start.time < $.event.special.swipe.durationThreshold &&
								Math.abs(start.coords[0] - stop.coords[0]) > $.event.special.swipe.horizontalDistanceThreshold &&
								Math.abs(start.coords[1] - stop.coords[1]) < $.event.special.swipe.verticalDistanceThreshold)
					        {

					            start.origin.trigger("swipe")
									.trigger(start.coords[0] > stop.coords[0] ? "swipeleft" : "swiperight");
					        }
					    }
					    start = stop = undefined;
					});
                });
            }
        };
        $.each({
            scrollstop: "scrollstart",
            taphold: "tap",
            swipeleft: "swipe",
            swiperight: "swipe"
        }, function(event, sourceEvent)
        {

            $.event.special[event] = {
                setup: function()
                {
                    $(this).bind(sourceEvent, $.noop);
                }
            };
        });

    })(jQuery, this);

    (function($, undefined)
    {
        $.extend($.support, {
            orientation: "orientation" in window && "onorientationchange" in window
        });
    } (jQuery));


    // throttled resize event
    (function($)
    {
        $.event.special.throttledresize = {
            setup: function()
            {
                $(this).bind("resize", handler);
            },
            teardown: function()
            {
                $(this).unbind("resize", handler);
            }
        };

        var throttle = 250,
			handler = function()
			{
			    curr = (new Date()).getTime();
			    diff = curr - lastCall;

			    if (diff >= throttle)
			    {

			        lastCall = curr;
			        $(this).trigger("throttledresize");

			    } else
			    {

			        if (heldCall)
			        {
			            clearTimeout(heldCall);
			        }

			        // Promise a held call will still execute
			        heldCall = setTimeout(handler, throttle - diff);
			    }
			},
			lastCall = 0,
			heldCall,
			curr,
			diff;
    })(jQuery);

    (function($, window)
    {
        var win = $(window),
		event_name = "orientationchange",
		special_event,
		get_orientation,
		last_orientation,
		initial_orientation_is_landscape,
		initial_orientation_is_default,
		portrait_map = { "0": true, "180": true };

        // It seems that some device/browser vendors use window.orientation values 0 and 180 to
        // denote the "default" orientation. For iOS devices, and most other smart-phones tested,
        // the default orientation is always "portrait", but in some Android and RIM based tablets,
        // the default orientation is "landscape". The following code attempts to use the window
        // dimensions to figure out what the current orientation is, and then makes adjustments
        // to the to the portrait_map if necessary, so that we can properly decode the
        // window.orientation value whenever get_orientation() is called.
        //
        // Note that we used to use a media query to figure out what the orientation the browser
        // thinks it is in:
        //
        //     initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");
        //
        // but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1,
        // where the browser *ALWAYS* applied the landscape media query. This bug does not
        // happen on iPad.

        if ($.support.orientation)
        {

            // Check the window width and height to figure out what the current orientation
            // of the device is at this moment. Note that we've initialized the portrait map
            // values to 0 and 180, *AND* we purposely check for landscape so that if we guess
            // wrong, , we default to the assumption that portrait is the default orientation.
            // We use a threshold check below because on some platforms like iOS, the iPhone
            // form-factor can report a larger width than height if the user turns on the
            // developer console. The actual threshold value is somewhat arbitrary, we just
            // need to make sure it is large enough to exclude the developer console case.

            var ww = window.innerWidth || $(window).width(),
			wh = window.innerHeight || $(window).height(),
			landscape_threshold = 50;

            initial_orientation_is_landscape = ww > wh && (ww - wh) > landscape_threshold;


            // Now check to see if the current window.orientation is 0 or 180.
            initial_orientation_is_default = portrait_map[window.orientation];

            // If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
            // if the initial orientation is portrait, but window.orientation reports 90 or -90, we
            // need to flip our portrait_map values because landscape is the default orientation for
            // this device/browser.
            if ((initial_orientation_is_landscape && initial_orientation_is_default) || (!initial_orientation_is_landscape && !initial_orientation_is_default))
            {
                portrait_map = { "-90": true, "90": true };
            }
        }

        $.event.special.orientationchange = $.extend({}, $.event.special.orientationchange, {
            setup: function()
            {
                // If the event is supported natively, return false so that jQuery
                // will bind to the event using DOM methods.
                if ($.support.orientation && !$.event.special.orientationchange.disabled)
                {
                    return false;
                }

                // Get the current orientation to avoid initial double-triggering.
                last_orientation = get_orientation();

                // Because the orientationchange event doesn't exist, simulate the
                // event by testing window dimensions on resize.
                win.bind("throttledresize", handler);
            },
            teardown: function()
            {
                // If the event is not supported natively, return false so that
                // jQuery will unbind the event using DOM methods.
                if ($.support.orientation && !$.event.special.orientationchange.disabled)
                {
                    return false;
                }

                // Because the orientationchange event doesn't exist, unbind the
                // resize event handler.
                win.unbind("throttledresize", handler);
            },
            add: function(handleObj)
            {
                // Save a reference to the bound event handler.
                var old_handler = handleObj.handler;


                handleObj.handler = function(event)
                {
                    // Modify event object, adding the .orientation property.
                    event.orientation = get_orientation();

                    // Call the originally-bound event handler and return its result.
                    return old_handler.apply(this, arguments);
                };
            }
        });

        // If the event is not supported natively, this handler will be bound to
        // the window resize event to simulate the orientationchange event.
        function handler()
        {
            // Get the current orientation.
            var orientation = get_orientation();

            if (orientation !== last_orientation)
            {
                // The orientation has changed, so trigger the orientationchange event.
                last_orientation = orientation;
                win.trigger(event_name);
            }
        }

        // Get the current page orientation. This method is exposed publicly, should it
        // be needed, as jQuery.event.special.orientationchange.orientation()
        $.event.special.orientationchange.orientation = get_orientation = function()
        {
            var isPortrait = true, elem = document.documentElement;

            // prefer window orientation to the calculation based on screensize as
            // the actual screen resize takes place before or after the orientation change event
            // has been fired depending on implementation (eg android 2.3 is before, iphone after).
            // More testing is required to determine if a more reliable method of determining the new screensize
            // is possible when orientationchange is fired. (eg, use media queries + element + opacity)
            if ($.support.orientation)
            {
                // if the window orientation registers as 0 or 180 degrees report
                // portrait, otherwise landscape
                isPortrait = portrait_map[window.orientation];
            } else
            {
                isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
            }

            return isPortrait ? "portrait" : "landscape";
        };

        $.fn[event_name] = function(fn)
        {
            return fn ? this.bind(event_name, fn) : this.trigger(event_name);
        };

        $.attrFn[event_name] = true;

    } (jQuery, this));


    (function($, undefined)
    {

        var $window = $(window),
	$html = $("html");

        /* $.mobile.media method: pass a CSS media type or query and get a bool return
        note: this feature relies on actual media query support for media queries, though types will work most anywhere
        examples:
        $.mobile.media('screen') // tests for screen media type
        $.mobile.media('screen and (min-width: 480px)') // tests for screen media type with window width > 480px
        $.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') // tests for webkit 2x pixel ratio (iPhone 4)
        */
        $.mobile.media = (function()
        {
            // TODO: use window.matchMedia once at least one UA implements it
            var cache = {},
		testDiv = $("<div id='jquery-mediatest'></div>"),
		fakeBody = $("<body>").append(testDiv);

            return function(query)
            {
                if (!(query in cache))
                {
                    var styleBlock = document.createElement("style"),
				cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";

                    //must set type for IE!
                    styleBlock.type = "text/css";

                    if (styleBlock.styleSheet)
                    {
                        styleBlock.styleSheet.cssText = cssrule;
                    } else
                    {
                        styleBlock.appendChild(document.createTextNode(cssrule));
                    }

                    $html.prepend(fakeBody).prepend(styleBlock);
                    cache[query] = testDiv.css("position") === "absolute";
                    fakeBody.add(styleBlock).remove();
                }
                return cache[query];
            };
        })();

    })(jQuery);

    (function($, undefined)
    {

        var fakeBody = $("<body>").prependTo("html"),
	fbCSS = fakeBody[0].style,
	vendors = ["Webkit", "Moz", "O"],
	webos = "palmGetResource" in window, //only used to rule out scrollTop
	opera = window.opera,
	operamini = window.operamini && ({}).toString.call(window.operamini) === "[object OperaMini]",
	bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB

        // thx Modernizr
        function propExists(prop)
        {
            var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1),
		props = (prop + " " + vendors.join(uc_prop + " ") + uc_prop).split(" ");

            for (var v in props)
            {
                if (fbCSS[props[v]] !== undefined)
                {
                    return true;
                }
            }
        }

        function validStyle(prop, value, check_vend)
        {
            var div = document.createElement('div'),
		uc = function(txt)
		{
		    return txt.charAt(0).toUpperCase() + txt.substr(1);
		},
		vend_pref = function(vend)
		{
		    return "-" + vend.charAt(0).toLowerCase() + vend.substr(1) + "-";
		},
		check_style = function(vend)
		{
		    var vend_prop = vend_pref(vend) + prop + ": " + value + ";",
				uc_vend = uc(vend),
				propStyle = uc_vend + uc(prop);

		    div.setAttribute("style", vend_prop);

		    if (!!div.style[propStyle])
		    {
		        ret = true;
		    }
		},
		check_vends = check_vend ? [check_vend] : vendors,
		ret;

            for (var i = 0; i < check_vends.length; i++)
            {
                check_style(check_vends[i]);
            }
            return !!ret;
        }

        // Thanks to Modernizr src for this test idea. `perspective` check is limited to Moz to prevent a false positive for 3D transforms on Android.
        function transform3dTest()
        {
            var prop = "transform-3d";
            return validStyle('perspective', '10px', 'moz') || $.mobile.media("(-" + vendors.join("-" + prop + "),(-") + "-" + prop + "),(" + prop + ")");
        }

        // Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
        function baseTagTest()
        {
            var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
		base = $("head base"),
		fauxEle = null,
		href = "",
		link, rebase;

            if (!base.length)
            {
                base = fauxEle = $("<base>", { "href": fauxBase }).appendTo("head");
            } else
            {
                href = base.attr("href");
            }

            link = $("<a href='testurl' />").prependTo(fakeBody);
            rebase = link[0].href;
            base[0].href = href || location.pathname;

            if (fauxEle)
            {
                fauxEle.remove();
            }
            return rebase.indexOf(fauxBase) === 0;
        }

        // Thanks Modernizr
        function cssPointerEventsTest()
        {
            var element = document.createElement('x'),
		documentElement = document.documentElement,
		getComputedStyle = window.getComputedStyle,
		supports;

            if (!('pointerEvents' in element.style))
            {
                return false;
            }

            element.style.pointerEvents = 'auto';
            element.style.pointerEvents = 'x';
            documentElement.appendChild(element);
            supports = getComputedStyle &&
    getComputedStyle(element, '').pointerEvents === 'auto';
            documentElement.removeChild(element);
            return !!supports;
        }


        // non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
        // allows for inclusion of IE 6+, including Windows Mobile 7
        $.extend($.mobile, { browser: {} });
        $.mobile.browser.ie = (function()
        {
            var v = 3,
		div = document.createElement("div"),
		a = div.all || [];

            do
            {
                div.innerHTML = "<!--[if gt IE " + (++v) + "]><br><![endif]-->";
            } while (a[0]);

            return v > 4 ? v : !v;
        })();


        $.extend($.support, {
            cssTransitions: "WebKitTransitionEvent" in window || validStyle('transition', 'height 100ms linear') && !opera,
            pushState: "pushState" in history && "replaceState" in history,
            mediaquery: $.mobile.media("only all"),
            cssPseudoElement: !!propExists("content"),
            touchOverflow: !!propExists("overflowScrolling"),
            cssTransform3d: transform3dTest(),
            boxShadow: !!propExists("boxShadow") && !bb,
            scrollTop: ("pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[0]) && !webos && !operamini,
            dynamicBaseTag: baseTagTest(),
            cssPointerEvents: cssPointerEventsTest()
        });

        fakeBody.remove();


        // $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
        // or that generally work better browsing in regular http for full page refreshes (Opera Mini)
        // Note: This detection below is used as a last resort.
        // We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
        var nokiaLTE7_3 = (function()
        {

            var ua = window.navigator.userAgent;

            //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
            return ua.indexOf("Nokia") > -1 &&
			(ua.indexOf("Symbian/3") > -1 || ua.indexOf("Series60/5") > -1) &&
			ua.indexOf("AppleWebKit") > -1 &&
			ua.match(/(BrowserNG|NokiaBrowser)\/7\.[0-3]/);
        })();

        // Support conditions that must be met in order to proceed
        // default enhanced qualifications are media query support OR IE 7+
        $.mobile.gradeA = function()
        {
            return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7;
        };

        $.mobile.ajaxBlacklist =
        // BlackBerry browsers, pre-webkit
			window.blackberry && !window.WebKitPoint ||
        // Opera Mini
			operamini ||
        // Symbian webkits pre 7.3
			nokiaLTE7_3;

        // Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
        // to render the stylesheets when they're referenced before this script, as we'd recommend doing.
        // This simply reappends the CSS in place, which for some reason makes it apply
        if (nokiaLTE7_3)
        {
            $(function()
            {
                $("head link[rel='stylesheet']").attr("rel", "alternate stylesheet").attr("rel", "stylesheet");
            });
        }

        // For ruling out shadows via css
        if (!$.support.boxShadow)
        {
            $("html").addClass("ui-mobile-nosupport-boxshadow");
        }

    })(jQuery);

    (function($, undefined)
    {

        $.widget("mobile.page", $.mobile.widget, {
            options: {
                theme: "c",
                domCache: false,
                keepNativeDefault: ":jqmData(role='none'), :jqmData(role='nojs')"
            },

            _create: function()
            {

                var self = this;

                // if false is returned by the callbacks do not create the page
                if (self._trigger("beforecreate") === false)
                {
                    return false;
                }

                self.element
			.attr("tabindex", "0")
			.addClass("ui-page ui-body-" + self.options.theme)
			.bind("pagebeforehide", function()
			{
			    self.removeContainerBackground();
			})
			.bind("pagebeforeshow", function()
			{
			    self.setContainerBackground();
			});

            },

            removeContainerBackground: function()
            {
                $.mobile.pageContainer.removeClass("ui-overlay-" + $.mobile.getInheritedTheme(this.element.parent()));
            },

            // set the page container background to the page theme
            setContainerBackground: function(theme)
            {
                if (this.options.theme)
                {
                    $.mobile.pageContainer.addClass("ui-overlay-" + (theme || this.options.theme));
                }
            },

            keepNativeSelector: function()
            {
                var options = this.options,
			keepNativeDefined = options.keepNative && $.trim(options.keepNative);

                if (keepNativeDefined && options.keepNative !== options.keepNativeDefault)
                {
                    return [options.keepNative, options.keepNativeDefault].join(", ");
                }

                return options.keepNativeDefault;
            }
        });
    })(jQuery);

    // Script: jQuery hashchange event
    // 
    // *Version: 1.3, Last updated: 7/21/2010*
    // 
    // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
    // GitHub       - http://github.com/cowboy/jquery-hashchange/
    // Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
    // (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
    // 
    // About: License
    // 
    // Copyright (c) 2010 "Cowboy" Ben Alman,
    // Dual licensed under the MIT and GPL licenses.
    // http://benalman.com/about/license/
    // 
    // About: Examples
    // 
    // These working examples, complete with fully commented code, illustrate a few
    // ways in which this plugin can be used.
    // 
    // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
    // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
    // 
    // About: Support and Testing
    // 
    // Information about what version or versions of jQuery this plugin has been
    // tested with, what browsers it has been tested in, and where the unit tests
    // reside (so you can test it yourself).
    // 
    // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
    // Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
    //                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
    // Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
    // 
    // About: Known issues
    // 
    // While this jQuery hashchange event implementation is quite stable and
    // robust, there are a few unfortunate browser bugs surrounding expected
    // hashchange event-based behaviors, independent of any JavaScript
    // window.onhashchange abstraction. See the following examples for more
    // information:
    // 
    // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
    // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
    // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
    // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
    // 
    // Also note that should a browser natively support the window.onhashchange 
    // event, but not report that it does, the fallback polling loop will be used.
    // 
    // About: Release History
    // 
    // 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
    //         "removable" for mobile-only development. Added IE6/7 document.title
    //         support. Attempted to make Iframe as hidden as possible by using
    //         techniques from http://www.paciellogroup.com/blog/?p=604. Added 
    //         support for the "shortcut" format $(window).hashchange( fn ) and
    //         $(window).hashchange() like jQuery provides for built-in events.
    //         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
    //         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
    //         and <jQuery.fn.hashchange.src> properties plus document-domain.html
    //         file to address access denied issues when setting document.domain in
    //         IE6/7.
    // 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
    //         from a page on another domain would cause an error in Safari 4. Also,
    //         IE6/7 Iframe is now inserted after the body (this actually works),
    //         which prevents the page from scrolling when the event is first bound.
    //         Event can also now be bound before DOM ready, but it won't be usable
    //         before then in IE6/7.
    // 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
    //         where browser version is incorrectly reported as 8.0, despite
    //         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
    // 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
    //         window.onhashchange functionality into a separate plugin for users
    //         who want just the basic event & back button support, without all the
    //         extra awesomeness that BBQ provides. This plugin will be included as
    //         part of jQuery BBQ, but also be available separately.

    (function($, window, undefined)
    {
        // Reused string.
        var str_hashchange = 'hashchange',

        // Method / object references.
    doc = document,
    fake_onhashchange,
    special = $.event.special,

        // Does the browser support window.onhashchange? Note that IE8 running in
        // IE7 compatibility mode reports true for 'onhashchange' in window, even
        // though the event isn't supported, so also test document.documentMode.
    doc_mode = doc.documentMode,
    supports_onhashchange = 'on' + str_hashchange in window && (doc_mode === undefined || doc_mode > 7);

        // Get location.hash (or what you'd expect location.hash to be) sans any
        // leading #. Thanks for making this necessary, Firefox!
        function get_fragment(url)
        {
            url = url || location.href;
            return '#' + url.replace(/^[^#]*#?(.*)$/, '$1');
        };

        // Method: jQuery.fn.hashchange
        // 
        // Bind a handler to the window.onhashchange event or trigger all bound
        // window.onhashchange event handlers. This behavior is consistent with
        // jQuery's built-in event handlers.
        // 
        // Usage:
        // 
        // > jQuery(window).hashchange( [ handler ] );
        // 
        // Arguments:
        // 
        //  handler - (Function) Optional handler to be bound to the hashchange
        //    event. This is a "shortcut" for the more verbose form:
        //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
        //    all bound window.onhashchange event handlers will be triggered. This
        //    is a shortcut for the more verbose
        //    jQuery(window).trigger( 'hashchange' ). These forms are described in
        //    the <hashchange event> section.
        // 
        // Returns:
        // 
        //  (jQuery) The initial jQuery collection of elements.

        // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
        // $(elem).hashchange() for triggering, like jQuery does for built-in events.
        $.fn[str_hashchange] = function(fn)
        {
            return fn ? this.bind(str_hashchange, fn) : this.trigger(str_hashchange);
        };

        // Property: jQuery.fn.hashchange.delay
        // 
        // The numeric interval (in milliseconds) at which the <hashchange event>
        // polling loop executes. Defaults to 50.

        // Property: jQuery.fn.hashchange.domain
        // 
        // If you're setting document.domain in your JavaScript, and you want hash
        // history to work in IE6/7, not only must this property be set, but you must
        // also set document.domain BEFORE jQuery is loaded into the page. This
        // property is only applicable if you are supporting IE6/7 (or IE8 operating
        // in "IE7 compatibility" mode).
        // 
        // In addition, the <jQuery.fn.hashchange.src> property must be set to the
        // path of the included "document-domain.html" file, which can be renamed or
        // modified if necessary (note that the document.domain specified must be the
        // same in both your main JavaScript as well as in this file).
        // 
        // Usage:
        // 
        // jQuery.fn.hashchange.domain = document.domain;

        // Property: jQuery.fn.hashchange.src
        // 
        // If, for some reason, you need to specify an Iframe src file (for example,
        // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
        // do so using this property. Note that when using this property, history
        // won't be recorded in IE6/7 until the Iframe src file loads. This property
        // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
        // compatibility" mode).
        // 
        // Usage:
        // 
        // jQuery.fn.hashchange.src = 'path/to/file.html';

        $.fn[str_hashchange].delay = 50;
        /*
        $.fn[ str_hashchange ].domain = null;
        $.fn[ str_hashchange ].src = null;
        */

        // Event: hashchange event
        // 
        // Fired when location.hash changes. In browsers that support it, the native
        // HTML5 window.onhashchange event is used, otherwise a polling loop is
        // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
        // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
        // compatibility" mode), a hidden Iframe is created to allow the back button
        // and hash-based history to work.
        // 
        // Usage as described in <jQuery.fn.hashchange>:
        // 
        // > // Bind an event handler.
        // > jQuery(window).hashchange( function(e) {
        // >   var hash = location.hash;
        // >   ...
        // > });
        // > 
        // > // Manually trigger the event handler.
        // > jQuery(window).hashchange();
        // 
        // A more verbose usage that allows for event namespacing:
        // 
        // > // Bind an event handler.
        // > jQuery(window).bind( 'hashchange', function(e) {
        // >   var hash = location.hash;
        // >   ...
        // > });
        // > 
        // > // Manually trigger the event handler.
        // > jQuery(window).trigger( 'hashchange' );
        // 
        // Additional Notes:
        // 
        // * The polling loop and Iframe are not created until at least one handler
        //   is actually bound to the 'hashchange' event.
        // * If you need the bound handler(s) to execute immediately, in cases where
        //   a location.hash exists on page load, via bookmark or page refresh for
        //   example, use jQuery(window).hashchange() or the more verbose 
        //   jQuery(window).trigger( 'hashchange' ).
        // * The event can be bound before DOM ready, but since it won't be usable
        //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
        //   to bind it inside a DOM ready handler.

        // Override existing $.event.special.hashchange methods (allowing this plugin
        // to be defined after jQuery BBQ in BBQ's source code).
        special[str_hashchange] = $.extend(special[str_hashchange], {

            // Called only when the first 'hashchange' event is bound to window.
            setup: function()
            {
                // If window.onhashchange is supported natively, there's nothing to do..
                if (supports_onhashchange) { return false; }

                // Otherwise, we need to create our own. And we don't want to call this
                // until the user binds to the event, just in case they never do, since it
                // will create a polling loop and possibly even a hidden Iframe.
                $(fake_onhashchange.start);
            },

            // Called only when the last 'hashchange' event is unbound from window.
            teardown: function()
            {
                // If window.onhashchange is supported natively, there's nothing to do..
                if (supports_onhashchange) { return false; }

                // Otherwise, we need to stop ours (if possible).
                $(fake_onhashchange.stop);
            }

        });

        // fake_onhashchange does all the work of triggering the window.onhashchange
        // event for browsers that don't natively support it, including creating a
        // polling loop to watch for hash changes and in IE 6/7 creating a hidden
        // Iframe to enable back and forward.
        fake_onhashchange = (function()
        {
            var self = {},
      timeout_id,

            // Remember the initial hash so it doesn't get triggered immediately.
      last_hash = get_fragment(),

      fn_retval = function(val) { return val; },
      history_set = fn_retval,
      history_get = fn_retval;

            // Start the polling loop.
            self.start = function()
            {
                timeout_id || poll();
            };

            // Stop the polling loop.
            self.stop = function()
            {
                timeout_id && clearTimeout(timeout_id);
                timeout_id = undefined;
            };

            // This polling loop checks every $.fn.hashchange.delay milliseconds to see
            // if location.hash has changed, and triggers the 'hashchange' event on
            // window when necessary.
            function poll()
            {
                var hash = get_fragment(),
        history_hash = history_get(last_hash);

                if (hash !== last_hash)
                {
                    history_set(last_hash = hash, history_hash);

                    $(window).trigger(str_hashchange);

                } else if (history_hash !== last_hash)
                {
                    location.href = location.href.replace(/#.*/, '') + history_hash;
                }

                timeout_id = setTimeout(poll, $.fn[str_hashchange].delay);
            };

            // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
            // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
            // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
            $.browser.msie && !supports_onhashchange && (function()
            {
                // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
                // when running in "IE7 compatibility" mode.

                var iframe,
        iframe_src;

                // When the event is bound and polling starts in IE 6/7, create a hidden
                // Iframe for history handling.
                self.start = function()
                {
                    if (!iframe)
                    {
                        iframe_src = $.fn[str_hashchange].src;
                        iframe_src = iframe_src && iframe_src + get_fragment();

                        // Create hidden Iframe. Attempt to make Iframe as hidden as possible
                        // by using techniques from http://www.paciellogroup.com/blog/?p=604.
                        iframe = $('<iframe tabindex="-1" title="empty"/>').hide()

                        // When Iframe has completely loaded, initialize the history and
                        // start polling.
            .one('load', function()
            {
                iframe_src || history_set(get_fragment());
                poll();
            })

                        // Load Iframe src if specified, otherwise nothing.
            .attr('src', iframe_src || 'javascript:0')

                        // Append Iframe after the end of the body to prevent unnecessary
                        // initial page scrolling (yes, this works).
            .insertAfter('body')[0].contentWindow;

                        // Whenever `document.title` changes, update the Iframe's title to
                        // prettify the back/next history menu entries. Since IE sometimes
                        // errors with "Unspecified error" the very first time this is set
                        // (yes, very useful) wrap this with a try/catch block.
                        doc.onpropertychange = function()
                        {
                            try
                            {
                                if (event.propertyName === 'title')
                                {
                                    iframe.document.title = doc.title;
                                }
                            } catch (e) { }
                        };

                    }
                };

                // Override the "stop" method since an IE6/7 Iframe was created. Even
                // if there are no longer any bound event handlers, the polling loop
                // is still necessary for back/next to work at all!
                self.stop = fn_retval;

                // Get history by looking at the hidden Iframe's location.hash.
                history_get = function()
                {
                    return get_fragment(iframe.location.href);
                };

                // Set a new history item by opening and then closing the Iframe
                // document, *then* setting its location.hash. If document.domain has
                // been set, update that as well.
                history_set = function(hash, history_hash)
                {
                    var iframe_doc = iframe.document,
          domain = $.fn[str_hashchange].domain;

                    if (hash !== history_hash)
                    {
                        // Update Iframe with any initial `document.title` that might be set.
                        iframe_doc.title = doc.title;

                        // Opening the Iframe's document after it has been closed is what
                        // actually adds a history entry.
                        iframe_doc.open();

                        // Set document.domain for the Iframe document as well, if necessary.
                        domain && iframe_doc.write('<script>document.domain="' + domain + '"</script>');

                        iframe_doc.close();

                        // Update the Iframe's hash, for great justice.
                        iframe.location.hash = hash;
                    }
                };

            })();
            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

            return self;
        })();

    })(jQuery, this);


    (function($, window, undefined)
    {

        var createHandler = function(sequential)
        {

            // Default to sequential
            if (sequential === undefined)
            {
                sequential = true;
            }

            return function(name, reverse, $to, $from)
            {

                var deferred = new $.Deferred(),
			reverseClass = reverse ? " reverse" : "",
			active = $.mobile.urlHistory.getActive(),
			toScroll = active.lastScroll || $.mobile.defaultHomeScroll,
			screenHeight = $.mobile.getScreenHeight(),
			maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $(window).width() > $.mobile.maxTransitionWidth,
			none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none" || Math.max($(window).scrollTop(), toScroll) > $.mobile.getMaxScrollForTransition(),
			toPreClass = " ui-page-pre-in",
			toggleViewportClass = function()
			{
			    $.mobile.pageContainer.toggleClass("ui-mobile-viewport-transitioning viewport-" + name);
			},
			scrollPage = function()
			{
			    // By using scrollTo instead of silentScroll, we can keep things better in order
			    // Just to be precautios, disable scrollstart listening like silentScroll would
			    $.event.special.scrollstart.enabled = false;

			    window.scrollTo(0, toScroll);

			    // reenable scrollstart listening like silentScroll would
			    setTimeout(function()
			    {
			        $.event.special.scrollstart.enabled = true;
			    }, 150);
			},
			cleanFrom = function()
			{
			    $from
					.removeClass($.mobile.activePageClass + " out in reverse " + name)
					.height("");
			},
			startOut = function()
			{
			    // if it's not sequential, call the doneOut transition to start the TO page animating in simultaneously
			    if (!sequential)
			    {
			        doneOut();
			    }
			    else
			    {
			        $from.animationComplete(doneOut);
			    }

			    // Set the from page's height and start it transitioning out
			    // Note: setting an explicit height helps eliminate tiling in the transitions
			    $from
					.height(screenHeight + $(window).scrollTop())
					.addClass(name + " out" + reverseClass);
			},

			doneOut = function()
			{

			    if ($from && sequential)
			    {
			        cleanFrom();
			    }

			    startIn();
			},

			startIn = function()
			{

			    // Prevent flickering in phonegap container: see comments at #4024 regarding iOS
			    $to.css("z-index", -10);

			    $to.addClass($.mobile.activePageClass + toPreClass);

			    // Send focus to page as it is now display: block
			    $.mobile.focusPage($to);

			    // Set to page height
			    $to.height(screenHeight + toScroll);

			    scrollPage();

			    // Restores visibility of the new page: added together with $to.css("z-index", -10);
			    $to.css("z-index", "");

			    if (!none)
			    {
			        $to.animationComplete(doneIn);
			    }

			    $to
					.removeClass(toPreClass)
					.addClass(name + " in" + reverseClass);

			    if (none)
			    {
			        doneIn();
			    }

			},

			doneIn = function()
			{

			    if (!sequential)
			    {

			        if ($from)
			        {
			            cleanFrom();
			        }
			    }

			    $to
					.removeClass("out in reverse " + name)
					.height("");

			    toggleViewportClass();

			    // In some browsers (iOS5), 3D transitions block the ability to scroll to the desired location during transition
			    // This ensures we jump to that spot after the fact, if we aren't there already.
			    if ($(window).scrollTop() !== toScroll)
			    {
			        scrollPage();
			    }

			    deferred.resolve(name, reverse, $to, $from, true);
			};

                toggleViewportClass();

                if ($from && !none)
                {
                    startOut();
                }
                else
                {
                    doneOut();
                }

                return deferred.promise();
            };
        };

        // generate the handlers from the above
        var sequentialHandler = createHandler(),
	simultaneousHandler = createHandler(false),
	defaultGetMaxScrollForTransition = function()
	{
	    return $.mobile.getScreenHeight() * 3;
	};

        // Make our transition handler the public default.
        $.mobile.defaultTransitionHandler = sequentialHandler;

        //transition handler dictionary for 3rd party transitions
        $.mobile.transitionHandlers = {
            "default": $.mobile.defaultTransitionHandler,
            "sequential": sequentialHandler,
            "simultaneous": simultaneousHandler
        };

        $.mobile.transitionFallbacks = {};

        // If transition is defined, check if css 3D transforms are supported, and if not, if a fallback is specified
        $.mobile._maybeDegradeTransition = function(transition)
        {
            if (transition && !$.support.cssTransform3d && $.mobile.transitionFallbacks[transition])
            {
                transition = $.mobile.transitionFallbacks[transition];
            }

            return transition;
        };

        // Set the getMaxScrollForTransition to default if no implementation was set by user
        $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defaultGetMaxScrollForTransition;
    })(jQuery, this);

    (function($, undefined)
    {

        //define vars for interal use
        var $window = $(window),
		$html = $('html'),
		$head = $('head'),

        //url path helpers for use in relative url management
		path = {

		    // This scary looking regular expression parses an absolute URL or its relative
		    // variants (protocol, site, document, query, and hash), into the various
		    // components (protocol, host, path, query, fragment, etc that make up the
		    // URL as well as some other commonly used sub-parts. When used with RegExp.exec()
		    // or String.match, it parses the URL into a results array that looks like this:
		    //
		    //     [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
		    //     [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
		    //     [2]: http://jblas:password@mycompany.com:8080/mail/inbox
		    //     [3]: http://jblas:password@mycompany.com:8080
		    //     [4]: http:
		    //     [5]: //
		    //     [6]: jblas:password@mycompany.com:8080
		    //     [7]: jblas:password
		    //     [8]: jblas
		    //     [9]: password
		    //    [10]: mycompany.com:8080
		    //    [11]: mycompany.com
		    //    [12]: 8080
		    //    [13]: /mail/inbox
		    //    [14]: /mail/
		    //    [15]: inbox
		    //    [16]: ?msg=1234&type=unread
		    //    [17]: #msg-content
		    //
		    urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,

		    //Parse a URL into a structure that allows easy access to
		    //all of the URL components by name.
		    parseUrl: function(url)
		    {
		        // If we're passed an object, we'll assume that it is
		        // a parsed url object and just return it back to the caller.
		        if ($.type(url) === "object")
		        {
		            return url;
		        }

		        var matches = path.urlParseRE.exec(url || "") || [];

		        // Create an object that allows the caller to access the sub-matches
		        // by name. Note that IE returns an empty string instead of undefined,
		        // like all other browsers do, so we normalize everything so its consistent
		        // no matter what browser we're running on.
		        return {
		            href: matches[0] || "",
		            hrefNoHash: matches[1] || "",
		            hrefNoSearch: matches[2] || "",
		            domain: matches[3] || "",
		            protocol: matches[4] || "",
		            doubleSlash: matches[5] || "",
		            authority: matches[6] || "",
		            username: matches[8] || "",
		            password: matches[9] || "",
		            host: matches[10] || "",
		            hostname: matches[11] || "",
		            port: matches[12] || "",
		            pathname: matches[13] || "",
		            directory: matches[14] || "",
		            filename: matches[15] || "",
		            search: matches[16] || "",
		            hash: matches[17] || ""
		        };
		    },

		    //Turn relPath into an asbolute path. absPath is
		    //an optional absolute path which describes what
		    //relPath is relative to.
		    makePathAbsolute: function(relPath, absPath)
		    {
		        if (relPath && relPath.charAt(0) === "/")
		        {
		            return relPath;
		        }

		        relPath = relPath || "";
		        absPath = absPath ? absPath.replace(/^\/|(\/[^\/]*|[^\/]+)$/g, "") : "";

		        var absStack = absPath ? absPath.split("/") : [],
					relStack = relPath.split("/");
		        for (var i = 0; i < relStack.length; i++)
		        {
		            var d = relStack[i];
		            switch (d)
		            {
		                case ".":
		                    break;
		                case "..":
		                    if (absStack.length)
		                    {
		                        absStack.pop();
		                    }
		                    break;
		                default:
		                    absStack.push(d);
		                    break;
		            }
		        }
		        return "/" + absStack.join("/");
		    },

		    //Returns true if both urls have the same domain.
		    isSameDomain: function(absUrl1, absUrl2)
		    {
		        return path.parseUrl(absUrl1).domain === path.parseUrl(absUrl2).domain;
		    },

		    //Returns true for any relative variant.
		    isRelativeUrl: function(url)
		    {
		        // All relative Url variants have one thing in common, no protocol.
		        return path.parseUrl(url).protocol === "";
		    },

		    //Returns true for an absolute url.
		    isAbsoluteUrl: function(url)
		    {
		        return path.parseUrl(url).protocol !== "";
		    },

		    //Turn the specified realtive URL into an absolute one. This function
		    //can handle all relative variants (protocol, site, document, query, fragment).
		    makeUrlAbsolute: function(relUrl, absUrl)
		    {
		        if (!path.isRelativeUrl(relUrl))
		        {
		            return relUrl;
		        }

		        var relObj = path.parseUrl(relUrl),
					absObj = path.parseUrl(absUrl),
					protocol = relObj.protocol || absObj.protocol,
					doubleSlash = relObj.protocol ? relObj.doubleSlash : (relObj.doubleSlash || absObj.doubleSlash),
					authority = relObj.authority || absObj.authority,
					hasPath = relObj.pathname !== "",
					pathname = path.makePathAbsolute(relObj.pathname || absObj.filename, absObj.pathname),
					search = relObj.search || (!hasPath && absObj.search) || "",
					hash = relObj.hash;

		        return protocol + doubleSlash + authority + pathname + search + hash;
		    },

		    //Add search (aka query) params to the specified url.
		    addSearchParams: function(url, params)
		    {
		        var u = path.parseUrl(url),
					p = (typeof params === "object") ? $.param(params) : params,
					s = u.search || "?";
		        return u.hrefNoSearch + s + (s.charAt(s.length - 1) !== "?" ? "&" : "") + p + (u.hash || "");
		    },

		    convertUrlToDataUrl: function(absUrl)
		    {
		        var u = path.parseUrl(absUrl);
		        if (path.isEmbeddedPage(u))
		        {
		            // For embedded pages, remove the dialog hash key as in getFilePath(),
		            // otherwise the Data Url won't match the id of the embedded Page.
		            return u.hash.split(dialogHashKey)[0].replace(/^#/, "");
		        } else if (path.isSameDomain(u, documentBase))
		        {
		            return u.hrefNoHash.replace(documentBase.domain, "").split(dialogHashKey)[0];
		        }

		        return window.decodeURIComponent(absUrl);
		    },

		    //get path from current hash, or from a file path
		    get: function(newPath)
		    {
		        if (newPath === undefined)
		        {
		            newPath = location.hash;
		        }
		        return path.stripHash(newPath).replace(/[^\/]*\.[^\/*]+$/, '');
		    },

		    //return the substring of a filepath before the sub-page key, for making a server request
		    getFilePath: function(path)
		    {
		        var splitkey = '&' + $.mobile.subPageUrlKey;
		        return path && path.split(splitkey)[0].split(dialogHashKey)[0];
		    },

		    //set location hash to path
		    set: function(path)
		    {
		        location.hash = path;
		    },

		    //test if a given url (string) is a path
		    //NOTE might be exceptionally naive
		    isPath: function(url)
		    {
		        return (/\//).test(url);
		    },

		    //return a url path with the window's location protocol/hostname/pathname removed
		    clean: function(url)
		    {
		        return url.replace(documentBase.domain, "");
		    },

		    //just return the url without an initial #
		    stripHash: function(url)
		    {
		        return url.replace(/^#/, "");
		    },

		    //remove the preceding hash, any query params, and dialog notations
		    cleanHash: function(hash)
		    {
		        return path.stripHash(hash.replace(/\?.*$/, "").replace(dialogHashKey, ""));
		    },

		    isHashValid: function(hash)
		    {
		        return (/^#[^#]+$/).test(hash);
		    },

		    //check whether a url is referencing the same domain, or an external domain or different protocol
		    //could be mailto, etc
		    isExternal: function(url)
		    {
		        var u = path.parseUrl(url);
		        return u.protocol && u.domain !== documentUrl.domain ? true : false;
		    },

		    hasProtocol: function(url)
		    {
		        return (/^(:?\w+:)/).test(url);
		    },

		    //check if the specified url refers to the first page in the main application document.
		    isFirstPageUrl: function(url)
		    {
		        // We only deal with absolute paths.
		        var u = path.parseUrl(path.makeUrlAbsolute(url, documentBase)),

		        // Does the url have the same path as the document?
					samePath = u.hrefNoHash === documentUrl.hrefNoHash || (documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash),

		        // Get the first page element.
					fp = $.mobile.firstPage,

		        // Get the id of the first page element if it has one.
					fpId = fp && fp[0] ? fp[0].id : undefined;

		        // The url refers to the first page if the path matches the document and
		        // it either has no hash value, or the hash is exactly equal to the id of the
		        // first page element.
		        return samePath && (!u.hash || u.hash === "#" || (fpId && u.hash.replace(/^#/, "") === fpId));
		    },

		    isEmbeddedPage: function(url)
		    {
		        var u = path.parseUrl(url);

		        //if the path is absolute, then we need to compare the url against
		        //both the documentUrl and the documentBase. The main reason for this
		        //is that links embedded within external documents will refer to the
		        //application document, whereas links embedded within the application
		        //document will be resolved against the document base.
		        if (u.protocol !== "")
		        {
		            return (u.hash && (u.hrefNoHash === documentUrl.hrefNoHash || (documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash)));
		        }
		        return (/^#/).test(u.href);
		    },


		    // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
		    // requests if the document doing the request was loaded via the file:// protocol.
		    // This is usually to allow the application to "phone home" and fetch app specific
		    // data. We normally let the browser handle external/cross-domain urls, but if the
		    // allowCrossDomainPages option is true, we will allow cross-domain http/https
		    // requests to go through our page loading logic.
		    isPermittedCrossDomainRequest: function(docUrl, reqUrl)
		    {
		        return $.mobile.allowCrossDomainPages &&
					docUrl.protocol === "file:" &&
					reqUrl.search(/^https?:/) !== -1;
		    }
		},

        //will be defined when a link is clicked and given an active class
		$activeClickedLink = null,

        //urlHistory is purely here to make guesses at whether the back or forward button was clicked
        //and provide an appropriate transition
		urlHistory = {
		    // Array of pages that are visited during a single page load.
		    // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs)
		    stack: [],

		    //maintain an index number for the active page in the stack
		    activeIndex: 0,

		    //get active
		    getActive: function()
		    {
		        return urlHistory.stack[urlHistory.activeIndex];
		    },

		    getPrev: function()
		    {
		        return urlHistory.stack[urlHistory.activeIndex - 1];
		    },

		    getNext: function()
		    {
		        return urlHistory.stack[urlHistory.activeIndex + 1];
		    },

		    // addNew is used whenever a new page is added
		    addNew: function(url, transition, title, pageUrl, role)
		    {
		        //if there's forward history, wipe it
		        if (urlHistory.getNext())
		        {
		            urlHistory.clearForward();
		        }

		        urlHistory.stack.push({ url: url, transition: transition, title: title, pageUrl: pageUrl, role: role });

		        urlHistory.activeIndex = urlHistory.stack.length - 1;
		    },

		    //wipe urls ahead of active index
		    clearForward: function()
		    {
		        urlHistory.stack = urlHistory.stack.slice(0, urlHistory.activeIndex + 1);
		    },

		    directHashChange: function(opts)
		    {
		        var back, forward, newActiveIndex, prev = this.getActive();

		        // check if url is in history and if it's ahead or behind current page
		        $.each(urlHistory.stack, function(i, historyEntry)
		        {

		            //if the url is in the stack, it's a forward or a back
		            if (opts.currentUrl === historyEntry.url)
		            {
		                //define back and forward by whether url is older or newer than current page
		                back = i < urlHistory.activeIndex;
		                forward = !back;
		                newActiveIndex = i;
		            }
		        });

		        // save new page index, null check to prevent falsey 0 result
		        this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;

		        if (back)
		        {
		            (opts.either || opts.isBack)(true);
		        } else if (forward)
		        {
		            (opts.either || opts.isForward)(false);
		        }
		    },

		    //disable hashchange event listener internally to ignore one change
		    //toggled internally when location.hash is updated to match the url of a successful page load
		    ignoreNextHashChange: false
		},

        //define first selector to receive focus when a page is shown
		focusable = "[tabindex],a,button:visible,select:visible,input",

        //queue to hold simultanious page transitions
		pageTransitionQueue = [],

        //indicates whether or not page is in process of transitioning
		isPageTransitioning = false,

        //nonsense hash change key for dialogs, so they create a history entry
		dialogHashKey = "&ui-state=dialog",

        //existing base tag?
		$base = $head.children("base"),

        //tuck away the original document URL minus any fragment.
		documentUrl = path.parseUrl(location.href),

        //if the document has an embedded base tag, documentBase is set to its
        //initial value. If a base tag does not exist, then we default to the documentUrl.
		documentBase = $base.length ? path.parseUrl(path.makeUrlAbsolute($base.attr("href"), documentUrl.href)) : documentUrl,

        //cache the comparison once.
		documentBaseDiffers = (documentUrl.hrefNoHash !== documentBase.hrefNoHash),

		getScreenHeight = $.mobile.getScreenHeight;

        //base element management, defined depending on dynamic base tag support
        var base = $.support.dynamicBaseTag ? {

            //define base element, for use in routing asset urls that are referenced in Ajax-requested markup
            element: ($base.length ? $base : $("<base>", { href: documentBase.hrefNoHash }).prependTo($head)),

            //set the generated BASE element's href attribute to a new page's base path
            set: function(href)
            {
                base.element.attr("href", path.makeUrlAbsolute(href, documentBase));
            },

            //set the generated BASE element's href attribute to a new page's base path
            reset: function()
            {
                base.element.attr("href", documentBase.hrefNoHash);
            }

        } : undefined;

        /*
        internal utility functions
        --------------------------------------*/


        //direct focus to the page title, or otherwise first focusable element
        $.mobile.focusPage = function(page)
        {
            var autofocus = page.find("[autofocus]"),
			pageTitle = page.find(".ui-title:eq(0)");

            if (autofocus.length)
            {
                autofocus.focus();
                return;
            }

            if (pageTitle.length)
            {
                pageTitle.focus();
            } else
            {
                page.focus();
            }
        };

        //remove active classes after page transition or error
        function removeActiveLinkClass(forceRemoval)
        {
            if (!!$activeClickedLink && (!$activeClickedLink.closest("." + $.mobile.activePageClass).length || forceRemoval))
            {
                $activeClickedLink.removeClass($.mobile.activeBtnClass);
            }
            $activeClickedLink = null;
        }

        function releasePageTransitionLock()
        {
            isPageTransitioning = false;
            if (pageTransitionQueue.length > 0)
            {
                $.mobile.changePage.apply(null, pageTransitionQueue.pop());
            }
        }

        // Save the last scroll distance per page, before it is hidden
        var setLastScrollEnabled = true,
		setLastScroll, delayedSetLastScroll;

        setLastScroll = function()
        {
            // this barrier prevents setting the scroll value based on the browser
            // scrolling the window based on a hashchange
            if (!setLastScrollEnabled)
            {
                return;
            }

            var active = $.mobile.urlHistory.getActive();

            if (active)
            {
                var lastScroll = $window.scrollTop();

                // Set active page's lastScroll prop.
                // If the location we're scrolling to is less than minScrollBack, let it go.
                active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
            }
        };

        // bind to scrollstop to gather scroll position. The delay allows for the hashchange
        // event to fire and disable scroll recording in the case where the browser scrolls
        // to the hash targets location (sometimes the top of the page). once pagechange fires
        // getLastScroll is again permitted to operate
        delayedSetLastScroll = function()
        {
            setTimeout(setLastScroll, 100);
        };

        // disable an scroll setting when a hashchange has been fired, this only works
        // because the recording of the scroll position is delayed for 100ms after
        // the browser might have changed the position because of the hashchange
        $window.bind($.support.pushState ? "popstate" : "hashchange", function()
        {
            setLastScrollEnabled = false;
        });

        // handle initial hashchange from chrome :(
        $window.one($.support.pushState ? "popstate" : "hashchange", function()
        {
            setLastScrollEnabled = true;
        });

        // wait until the mobile page container has been determined to bind to pagechange
        $window.one("pagecontainercreate", function()
        {
            // once the page has changed, re-enable the scroll recording
            $.mobile.pageContainer.bind("pagechange", function()
            {

                setLastScrollEnabled = true;

                // remove any binding that previously existed on the get scroll
                // which may or may not be different than the scroll element determined for
                // this page previously
                $window.unbind("scrollstop", delayedSetLastScroll);

                // determine and bind to the current scoll element which may be the window
                // or in the case of touch overflow the element with touch overflow
                $window.bind("scrollstop", delayedSetLastScroll);
            });
        });

        // bind to scrollstop for the first page as "pagechange" won't be fired in that case
        $window.bind("scrollstop", delayedSetLastScroll);

        // No-op implementation of transition degradation
        $.mobile._maybeDegradeTransition = $.mobile._maybeDegradeTransition || function(transition)
        {
            return transition;
        };

        //function for transitioning between two existing pages
        function transitionPages(toPage, fromPage, transition, reverse)
        {

            if (fromPage)
            {
                //trigger before show/hide events
                fromPage.data("page")._trigger("beforehide", null, { nextPage: toPage });
            }

            toPage.data("page")._trigger("beforeshow", null, { prevPage: fromPage || $("") });

            //clear page loader
            $.mobile.hidePageLoadingMsg();

            transition = $.mobile._maybeDegradeTransition(transition);

            //find the transition handler for the specified transition. If there
            //isn't one in our transitionHandlers dictionary, use the default one.
            //call the handler immediately to kick-off the transition.
            var th = $.mobile.transitionHandlers[transition || "default"] || $.mobile.defaultTransitionHandler,
			promise = th(transition, reverse, toPage, fromPage);

            promise.done(function()
            {

                //trigger show/hide events
                if (fromPage)
                {
                    fromPage.data("page")._trigger("hide", null, { nextPage: toPage });
                }

                //trigger pageshow, define prevPage as either fromPage or empty jQuery obj
                toPage.data("page")._trigger("show", null, { prevPage: fromPage || $("") });
            });

            return promise;
        }

        //simply set the active page's minimum height to screen height, depending on orientation
        function resetActivePageHeight()
        {
            var aPage = $("." + $.mobile.activePageClass),
			aPagePadT = parseFloat(aPage.css("padding-top")),
			aPagePadB = parseFloat(aPage.css("padding-bottom")),
			aPageBorderT = parseFloat(aPage.css("border-top-width")),
			aPageBorderB = parseFloat(aPage.css("border-bottom-width"));

            aPage.css("min-height", getScreenHeight() - aPagePadT - aPagePadB - aPageBorderT - aPageBorderB);
        }

        //shared page enhancements
        function enhancePage($page, role)
        {
            // If a role was specified, make sure the data-role attribute
            // on the page element is in sync.
            if (role)
            {
                $page.attr("data-" + $.mobile.ns + "role", role);
            }

            //run page plugin
            $page.page();
        }

        /* exposed $.mobile methods */

        //animation complete callback
        $.fn.animationComplete = function(callback)
        {
            if ($.support.cssTransitions)
            {
                return $(this).one('webkitAnimationEnd animationend', callback);
            }
            else
            {
                // defer execution for consistency between webkit/non webkit
                setTimeout(callback, 0);
                return $(this);
            }
        };

        //expose path object on $.mobile
        $.mobile.path = path;

        //expose base object on $.mobile
        $.mobile.base = base;

        //history stack
        $.mobile.urlHistory = urlHistory;

        $.mobile.dialogHashKey = dialogHashKey;



        //enable cross-domain page support
        $.mobile.allowCrossDomainPages = false;

        //return the original document url
        $.mobile.getDocumentUrl = function(asParsedObject)
        {
            return asParsedObject ? $.extend({}, documentUrl) : documentUrl.href;
        };

        //return the original document base url
        $.mobile.getDocumentBase = function(asParsedObject)
        {
            return asParsedObject ? $.extend({}, documentBase) : documentBase.href;
        };

        $.mobile._bindPageRemove = function()
        {
            var page = $(this);

            // when dom caching is not enabled or the page is embedded bind to remove the page on hide
            if (!page.data("page").options.domCache &&
				page.is(":jqmData(external-page='true')"))
            {

                page.bind('pagehide.remove', function()
                {
                    var $this = $(this),
					prEvent = new $.Event("pageremove");

                    $this.trigger(prEvent);

                    if (!prEvent.isDefaultPrevented())
                    {
                        $this.removeWithDependents();
                    }
                });
            }
        };

        // Load a page into the DOM.
        $.mobile.loadPage = function(url, options)
        {
            // This function uses deferred notifications to let callers
            // know when the page is done loading, or if an error has occurred.
            var deferred = $.Deferred(),

            // The default loadPage options with overrides specified by
            // the caller.
			settings = $.extend({}, $.mobile.loadPage.defaults, options),

            // The DOM element for the page after it has been loaded.
			page = null,

            // If the reloadPage option is true, and the page is already
            // in the DOM, dupCachedPage will be set to the page element
            // so that it can be removed after the new version of the
            // page is loaded off the network.
			dupCachedPage = null,

            // determine the current base url
			findBaseWithDefault = function()
			{
			    var closestBase = ($.mobile.activePage && getClosestBaseUrl($.mobile.activePage));
			    return closestBase || documentBase.hrefNoHash;
			},

            // The absolute version of the URL passed into the function. This
            // version of the URL may contain dialog/subpage params in it.
			absUrl = path.makeUrlAbsolute(url, findBaseWithDefault());


            // If the caller provided data, and we're using "get" request,
            // append the data to the URL.
            if (settings.data && settings.type === "get")
            {
                absUrl = path.addSearchParams(absUrl, settings.data);
                settings.data = undefined;
            }

            // If the caller is using a "post" request, reloadPage must be true
            if (settings.data && settings.type === "post")
            {
                settings.reloadPage = true;
            }

            // The absolute version of the URL minus any dialog/subpage params.
            // In otherwords the real URL of the page to be loaded.
            var fileUrl = path.getFilePath(absUrl),

            // The version of the Url actually stored in the data-url attribute of
            // the page. For embedded pages, it is just the id of the page. For pages
            // within the same domain as the document base, it is the site relative
            // path. For cross-domain pages (Phone Gap only) the entire absolute Url
            // used to load the page.
			dataUrl = path.convertUrlToDataUrl(absUrl);

            // Make sure we have a pageContainer to work with.
            settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;

            // Check to see if the page already exists in the DOM.
            page = settings.pageContainer.children(":jqmData(url='" + dataUrl + "')");

            // If we failed to find the page, check to see if the url is a
            // reference to an embedded page. If so, it may have been dynamically
            // injected by a developer, in which case it would be lacking a data-url
            // attribute and in need of enhancement.
            if (page.length === 0 && dataUrl && !path.isPath(dataUrl))
            {
                page = settings.pageContainer.children("#" + dataUrl)
				.attr("data-" + $.mobile.ns + "url", dataUrl)
				.jqmData("url", dataUrl);
            }

            // If we failed to find a page in the DOM, check the URL to see if it
            // refers to the first page in the application. If it isn't a reference
            // to the first page and refers to non-existent embedded page, error out.
            if (page.length === 0)
            {
                if ($.mobile.firstPage && path.isFirstPageUrl(fileUrl))
                {
                    // Check to make sure our cached-first-page is actually
                    // in the DOM. Some user deployed apps are pruning the first
                    // page from the DOM for various reasons, we check for this
                    // case here because we don't want a first-page with an id
                    // falling through to the non-existent embedded page error
                    // case. If the first-page is not in the DOM, then we let
                    // things fall through to the ajax loading code below so
                    // that it gets reloaded.
                    if ($.mobile.firstPage.parent().length)
                    {
                        page = $($.mobile.firstPage);
                    }
                } else if (path.isEmbeddedPage(fileUrl))
                {
                    deferred.reject(absUrl, options);
                    return deferred.promise();
                }
            }

            // Reset base to the default document base.
            if (base)
            {
                base.reset();
            }

            // If the page we are interested in is already in the DOM,
            // and the caller did not indicate that we should force a
            // reload of the file, we are done. Otherwise, track the
            // existing page as a duplicated.
            if (page.length)
            {
                if (!settings.reloadPage)
                {
                    enhancePage(page, settings.role);
                    deferred.resolve(absUrl, options, page);
                    return deferred.promise();
                }
                dupCachedPage = page;
            }

            var mpc = settings.pageContainer,
			pblEvent = new $.Event("pagebeforeload"),
			triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings };

            // Let listeners know we're about to load a page.
            mpc.trigger(pblEvent, triggerData);

            // If the default behavior is prevented, stop here!
            if (pblEvent.isDefaultPrevented())
            {
                return deferred.promise();
            }

            if (settings.showLoadMsg)
            {

                // This configurable timeout allows cached pages a brief delay to load without showing a message
                var loadMsgDelay = setTimeout(function()
                {
                    $.mobile.showPageLoadingMsg();
                }, settings.loadMsgDelay),

                // Shared logic for clearing timeout and removing message.
				hideMsg = function()
				{

				    // Stop message show timer
				    clearTimeout(loadMsgDelay);

				    // Hide loading message
				    $.mobile.hidePageLoadingMsg();
				};
            }

            if (!($.mobile.allowCrossDomainPages || path.isSameDomain(documentUrl, absUrl)))
            {
                deferred.reject(absUrl, options);
            } else
            {
                // Load the new page.
                $.ajax({
                    url: fileUrl,
                    type: settings.type,
                    data: settings.data,
                    dataType: "html",
                    success: function(html, textStatus, xhr)
                    {
                        //pre-parse html to check for a data-url,
                        //use it as the new fileUrl, base path, etc
                        var all = $("<div></div>"),

                        //page title regexp
						newPageTitle = html.match(/<title[^>]*>([^<]*)/) && RegExp.$1,

                        // TODO handle dialogs again
						pageElemRegex = new RegExp("(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)"),
						dataUrlRegex = new RegExp("\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?");


                        // data-url must be provided for the base tag so resource requests can be directed to the
                        // correct url. loading into a temprorary element makes these requests immediately
                        if (pageElemRegex.test(html) &&
							RegExp.$1 &&
							dataUrlRegex.test(RegExp.$1) &&
							RegExp.$1)
                        {
                            url = fileUrl = path.getFilePath($("<div>" + RegExp.$1 + "</div>").text());
                        }

                        if (base)
                        {
                            base.set(fileUrl);
                        }

                        //workaround to allow scripts to execute when included in page divs
                        all.get(0).innerHTML = html;
                        page = all.find(":jqmData(role='page'), :jqmData(role='dialog')").first();

                        //if page elem couldn't be found, create one and insert the body element's contents
                        if (!page.length)
                        {
                            page = $("<div data-" + $.mobile.ns + "role='page'>" + html.split(/<\/?body[^>]*>/gmi)[1] + "</div>");
                        }

                        if (newPageTitle && !page.jqmData("title"))
                        {
                            if (~newPageTitle.indexOf("&"))
                            {
                                newPageTitle = $("<div>" + newPageTitle + "</div>").text();
                            }
                            page.jqmData("title", newPageTitle);
                        }

                        //rewrite src and href attrs to use a base url
                        if (!$.support.dynamicBaseTag)
                        {
                            var newPath = path.get(fileUrl);
                            page.find("[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]").each(function()
                            {
                                var thisAttr = $(this).is('[href]') ? 'href' :
									$(this).is('[src]') ? 'src' : 'action',
								thisUrl = $(this).attr(thisAttr);

                                // XXX_jblas: We need to fix this so that it removes the document
                                //            base URL, and then prepends with the new page URL.
                                //if full path exists and is same, chop it - helps IE out
                                thisUrl = thisUrl.replace(location.protocol + '//' + location.host + location.pathname, '');

                                if (!/^(\w+:|#|\/)/.test(thisUrl))
                                {
                                    $(this).attr(thisAttr, newPath + thisUrl);
                                }
                            });
                        }

                        //append to page and enhance
                        // TODO taging a page with external to make sure that embedded pages aren't removed
                        //      by the various page handling code is bad. Having page handling code in many
                        //      places is bad. Solutions post 1.0
                        page
						.attr("data-" + $.mobile.ns + "url", path.convertUrlToDataUrl(fileUrl))
						.attr("data-" + $.mobile.ns + "external-page", true)
						.appendTo(settings.pageContainer);

                        // wait for page creation to leverage options defined on widget
                        page.one('pagecreate', $.mobile._bindPageRemove);

                        enhancePage(page, settings.role);

                        // Enhancing the page may result in new dialogs/sub pages being inserted
                        // into the DOM. If the original absUrl refers to a sub-page, that is the
                        // real page we are interested in.
                        if (absUrl.indexOf("&" + $.mobile.subPageUrlKey) > -1)
                        {
                            page = settings.pageContainer.children(":jqmData(url='" + dataUrl + "')");
                        }

                        //bind pageHide to removePage after it's hidden, if the page options specify to do so

                        // Remove loading message.
                        if (settings.showLoadMsg)
                        {
                            hideMsg();
                        }

                        // Add the page reference and xhr to our triggerData.
                        triggerData.xhr = xhr;
                        triggerData.textStatus = textStatus;
                        triggerData.page = page;

                        // Let listeners know the page loaded successfully.
                        settings.pageContainer.trigger("pageload", triggerData);

                        deferred.resolve(absUrl, options, page, dupCachedPage);
                    },
                    error: function(xhr, textStatus, errorThrown)
                    {
                        //set base back to current path
                        if (base)
                        {
                            base.set(path.get());
                        }

                        // Add error info to our triggerData.
                        triggerData.xhr = xhr;
                        triggerData.textStatus = textStatus;
                        triggerData.errorThrown = errorThrown;

                        var plfEvent = new $.Event("pageloadfailed");

                        // Let listeners know the page load failed.
                        settings.pageContainer.trigger(plfEvent, triggerData);

                        // If the default behavior is prevented, stop here!
                        // Note that it is the responsibility of the listener/handler
                        // that called preventDefault(), to resolve/reject the
                        // deferred object within the triggerData.
                        if (plfEvent.isDefaultPrevented())
                        {
                            return;
                        }

                        // Remove loading message.
                        if (settings.showLoadMsg)
                        {

                            // Remove loading message.
                            hideMsg();

                            // show error message
                            $.mobile.showPageLoadingMsg($.mobile.pageLoadErrorMessageTheme, $.mobile.pageLoadErrorMessage, true);

                            // hide after delay
                            setTimeout($.mobile.hidePageLoadingMsg, 1500);
                        }

                        deferred.reject(absUrl, options);
                    }
                });
            }

            return deferred.promise();
        };

        $.mobile.loadPage.defaults = {
            type: "get",
            data: undefined,
            reloadPage: false,
            role: undefined, // By default we rely on the role defined by the @data-role attribute.
            showLoadMsg: false,
            pageContainer: undefined,
            loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message.
        };

        // Show a specific page in the page container.
        $.mobile.changePage = function(toPage, options)
        {
            // If we are in the midst of a transition, queue the current request.
            // We'll call changePage() once we're done with the current transition to
            // service the request.
            if (isPageTransitioning)
            {
                pageTransitionQueue.unshift(arguments);
                return;
            }

            var settings = $.extend({}, $.mobile.changePage.defaults, options);

            // Make sure we have a pageContainer to work with.
            settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;

            // Make sure we have a fromPage.
            settings.fromPage = settings.fromPage || $.mobile.activePage;

            var mpc = settings.pageContainer,
			pbcEvent = new $.Event("pagebeforechange"),
			triggerData = { toPage: toPage, options: settings };

            // Let listeners know we're about to change the current page.
            mpc.trigger(pbcEvent, triggerData);

            // If the default behavior is prevented, stop here!
            if (pbcEvent.isDefaultPrevented())
            {
                return;
            }

            // We allow "pagebeforechange" observers to modify the toPage in the trigger
            // data to allow for redirects. Make sure our toPage is updated.

            toPage = triggerData.toPage;

            // Set the isPageTransitioning flag to prevent any requests from
            // entering this method while we are in the midst of loading a page
            // or transitioning.

            isPageTransitioning = true;

            // If the caller passed us a url, call loadPage()
            // to make sure it is loaded into the DOM. We'll listen
            // to the promise object it returns so we know when
            // it is done loading or if an error ocurred.
            if (typeof toPage === "string")
            {
                $.mobile.loadPage(toPage, settings)
				.done(function(url, options, newPage, dupCachedPage)
				{
				    isPageTransitioning = false;
				    options.duplicateCachedPage = dupCachedPage;
				    $.mobile.changePage(newPage, options);
				})
				.fail(function(url, options)
				{
				    isPageTransitioning = false;

				    //clear out the active button state
				    removeActiveLinkClass(true);

				    //release transition lock so navigation is free again
				    releasePageTransitionLock();
				    settings.pageContainer.trigger("pagechangefailed", triggerData);
				});
                return;
            }

            // If we are going to the first-page of the application, we need to make
            // sure settings.dataUrl is set to the application document url. This allows
            // us to avoid generating a document url with an id hash in the case where the
            // first-page of the document has an id attribute specified.
            if (toPage[0] === $.mobile.firstPage[0] && !settings.dataUrl)
            {
                settings.dataUrl = documentUrl.hrefNoHash;
            }

            // The caller passed us a real page DOM element. Update our
            // internal state and then trigger a transition to the page.
            var fromPage = settings.fromPage,
			url = (settings.dataUrl && path.convertUrlToDataUrl(settings.dataUrl)) || toPage.jqmData("url"),
            // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
			pageUrl = url,
			fileUrl = path.getFilePath(url),
			active = urlHistory.getActive(),
			activeIsInitialPage = urlHistory.activeIndex === 0,
			historyDir = 0,
			pageTitle = document.title,
			isDialog = settings.role === "dialog" || toPage.jqmData("role") === "dialog";

            // By default, we prevent changePage requests when the fromPage and toPage
            // are the same element, but folks that generate content manually/dynamically
            // and reuse pages want to be able to transition to the same page. To allow
            // this, they will need to change the default value of allowSamePageTransition
            // to true, *OR*, pass it in as an option when they manually call changePage().
            // It should be noted that our default transition animations assume that the
            // formPage and toPage are different elements, so they may behave unexpectedly.
            // It is up to the developer that turns on the allowSamePageTransitiona option
            // to either turn off transition animations, or make sure that an appropriate
            // animation transition is used.
            if (fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition)
            {
                isPageTransitioning = false;
                mpc.trigger("pagechange", triggerData);

                // Even if there is no page change to be done, we should keep the urlHistory in sync with the hash changes
                if (settings.fromHashChange)
                {
                    urlHistory.directHashChange({
                        currentUrl: url,
                        isBack: function() { },
                        isForward: function() { }
                    });
                }

                return;
            }

            // We need to make sure the page we are given has already been enhanced.
            enhancePage(toPage, settings.role);

            // If the changePage request was sent from a hashChange event, check to see if the
            // page is already within the urlHistory stack. If so, we'll assume the user hit
            // the forward/back button and will try to match the transition accordingly.
            if (settings.fromHashChange)
            {
                urlHistory.directHashChange({
                    currentUrl: url,
                    isBack: function() { historyDir = -1; },
                    isForward: function() { historyDir = 1; }
                });
            }

            // Kill the keyboard.
            // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead,
            //            we should be tracking focus with a delegate() handler so we already have
            //            the element in hand at this point.
            // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement
            // is undefined when we are in an IFrame.
            try
            {
                if (document.activeElement && document.activeElement.nodeName.toLowerCase() !== 'body')
                {
                    $(document.activeElement).blur();
                } else
                {
                    $("input:focus, textarea:focus, select:focus").blur();
                }
            } catch (e) { }

            // Record whether we are at a place in history where a dialog used to be - if so, do not add a new history entry and do not change the hash either
            var alreadyThere = false;

            // If we're displaying the page as a dialog, we don't want the url
            // for the dialog content to be used in the hash. Instead, we want
            // to append the dialogHashKey to the url of the current page.
            if (isDialog && active)
            {
                // on the initial page load active.url is undefined and in that case should
                // be an empty string. Moving the undefined -> empty string back into
                // urlHistory.addNew seemed imprudent given undefined better represents
                // the url state

                // If we are at a place in history that once belonged to a dialog, reuse
                // this state without adding to urlHistory and without modifying the hash.
                // However, if a dialog is already displayed at this point, and we're
                // about to display another dialog, then we must add another hash and
                // history entry on top so that one may navigate back to the original dialog
                if (active.url.indexOf(dialogHashKey) > -1 && !$.mobile.activePage.is(".ui-dialog"))
                {
                    settings.changeHash = false;
                    alreadyThere = true;
                }

                url = (active.url || "") + dialogHashKey;

                // tack on another dialogHashKey if this is the same as the initial hash
                // this makes sure that a history entry is created for this dialog
                if (urlHistory.activeIndex === 0 && url === urlHistory.initialDst)
                {
                    url += dialogHashKey;
                }
            }

            // Set the location hash.
            if (settings.changeHash !== false && url)
            {
                //disable hash listening temporarily
                urlHistory.ignoreNextHashChange = true;
                //update hash and history
                path.set(url);
            }

            // if title element wasn't found, try the page div data attr too
            // If this is a deep-link or a reload ( active === undefined ) then just use pageTitle
            var newPageTitle = (!active) ? pageTitle : toPage.jqmData("title") || toPage.children(":jqmData(role='header')").find(".ui-title").getEncodedText();
            if (!!newPageTitle && pageTitle === document.title)
            {
                pageTitle = newPageTitle;
            }
            if (!toPage.jqmData("title"))
            {
                toPage.jqmData("title", pageTitle);
            }

            // Make sure we have a transition defined.
            settings.transition = settings.transition ||
			((historyDir && !activeIsInitialPage) ? active.transition : undefined) ||
			(isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition);

            //add page to history stack if it's not back or forward
            if (!historyDir && !alreadyThere)
            {
                urlHistory.addNew(url, settings.transition, pageTitle, pageUrl, settings.role);
            }

            //set page title
            document.title = urlHistory.getActive().title;

            //set "toPage" as activePage
            $.mobile.activePage = toPage;

            // If we're navigating back in the URL history, set reverse accordingly.
            settings.reverse = settings.reverse || historyDir < 0;

            transitionPages(toPage, fromPage, settings.transition, settings.reverse)
			.done(function(name, reverse, $to, $from, alreadyFocused)
			{
			    removeActiveLinkClass();

			    //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
			    if (settings.duplicateCachedPage)
			    {
			        settings.duplicateCachedPage.remove();
			    }

			    // Send focus to the newly shown page. Moved from promise .done binding in transitionPages
			    // itself to avoid ie bug that reports offsetWidth as > 0 (core check for visibility)
			    // despite visibility: hidden addresses issue #2965
			    // https://github.com/jquery/jquery-mobile/issues/2965
			    if (!alreadyFocused)
			    {
			        $.mobile.focusPage(toPage);
			    }

			    releasePageTransitionLock();

			    // Let listeners know we're all done changing the current page.
			    mpc.trigger("pagechange", triggerData);
			});
        };

        $.mobile.changePage.defaults = {
            transition: undefined,
            reverse: false,
            changeHash: true,
            fromHashChange: false,
            role: undefined, // By default we rely on the role defined by the @data-role attribute.
            duplicateCachedPage: undefined,
            pageContainer: undefined,
            showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
            dataUrl: undefined,
            fromPage: undefined,
            allowSamePageTransition: false
        };

        /* Event Bindings - hashchange, submit, and click */
        function findClosestLink(ele)
        {
            while (ele)
            {
                // Look for the closest element with a nodeName of "a".
                // Note that we are checking if we have a valid nodeName
                // before attempting to access it. This is because the
                // node we get called with could have originated from within
                // an embedded SVG document where some symbol instance elements
                // don't have nodeName defined on them, or strings are of type
                // SVGAnimatedString.
                if ((typeof ele.nodeName === "string") && ele.nodeName.toLowerCase() === "a")
                {
                    break;
                }
                ele = ele.parentNode;
            }
            return ele;
        }

        // The base URL for any given element depends on the page it resides in.
        function getClosestBaseUrl(ele)
        {
            // Find the closest page and extract out its url.
            var url = $(ele).closest(".ui-page").jqmData("url"),
			base = documentBase.hrefNoHash;

            if (!url || !path.isPath(url))
            {
                url = base;
            }

            return path.makeUrlAbsolute(url, base);
        }

        //The following event bindings should be bound after mobileinit has been triggered
        //the following deferred is resolved in the init file
        $.mobile.navreadyDeferred = $.Deferred();
        $.mobile.navreadyDeferred.done(function()
        {
            //bind to form submit events, handle with Ajax
            $(document).delegate("form", "submit", function(event)
            {
                var $this = $(this);

                if (!$.mobile.ajaxEnabled ||
                // test that the form is, itself, ajax false
					$this.is(":jqmData(ajax='false')") ||
                // test that $.mobile.ignoreContentEnabled is set and
                // the form or one of it's parents is ajax=false
					!$this.jqmHijackable().length)
                {
                    return;
                }

                var type = $this.attr("method"),
				target = $this.attr("target"),
				url = $this.attr("action");

                // If no action is specified, browsers default to using the
                // URL of the document containing the form. Since we dynamically
                // pull in pages from external documents, the form should submit
                // to the URL for the source document of the page containing
                // the form.
                if (!url)
                {
                    // Get the @data-url for the page containing the form.
                    url = getClosestBaseUrl($this);
                    if (url === documentBase.hrefNoHash)
                    {
                        // The url we got back matches the document base,
                        // which means the page must be an internal/embedded page,
                        // so default to using the actual document url as a browser
                        // would.
                        url = documentUrl.hrefNoSearch;
                    }
                }

                url = path.makeUrlAbsolute(url, getClosestBaseUrl($this));

                if ((path.isExternal(url) && !path.isPermittedCrossDomainRequest(documentUrl, url)) || target)
                {
                    return;
                }

                $.mobile.changePage(
				url,
				{
				    type: type && type.length && type.toLowerCase() || "get",
				    data: $this.serialize(),
				    transition: $this.jqmData("transition"),
				    reverse: $this.jqmData("direction") === "reverse",
				    reloadPage: true
				}
			);
                event.preventDefault();
            });

            //add active state on vclick
            $(document).bind("vclick", function(event)
            {
                // if this isn't a left click we don't care. Its important to note
                // that when the virtual event is generated it will create the which attr
                if (event.which > 1 || !$.mobile.linkBindingEnabled)
                {
                    return;
                }

                var link = findClosestLink(event.target);

                // split from the previous return logic to avoid find closest where possible
                // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
                // can be avoided
                if (!$(link).jqmHijackable().length)
                {
                    return;
                }

                if (link)
                {
                    if (path.parseUrl(link.getAttribute("href") || "#").hash !== "#")
                    {
                        removeActiveLinkClass(true);
                        $activeClickedLink = $(link).closest(".ui-btn").not(".ui-disabled");
                        $activeClickedLink.addClass($.mobile.activeBtnClass);
                    }
                }
            });

            // click routing - direct to HTTP or Ajax, accordingly
            $(document).bind("click", function(event)
            {
                if (!$.mobile.linkBindingEnabled)
                {
                    return;
                }

                var link = findClosestLink(event.target), $link = $(link), httpCleanup;

                // If there is no link associated with the click or its not a left
                // click we want to ignore the click
                // TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
                // can be avoided
                if (!link || event.which > 1 || !$link.jqmHijackable().length)
                {
                    return;
                }

                //remove active link class if external (then it won't be there if you come back)
                httpCleanup = function()
                {
                    window.setTimeout(function() { removeActiveLinkClass(true); }, 200);
                };

                //if there's a data-rel=back attr, go back in history
                if ($link.is(":jqmData(rel='back')"))
                {
                    window.history.back();
                    return false;
                }

                var baseUrl = getClosestBaseUrl($link),

                //get href, if defined, otherwise default to empty hash
				href = path.makeUrlAbsolute($link.attr("href") || "#", baseUrl);

                //if ajax is disabled, exit early
                if (!$.mobile.ajaxEnabled && !path.isEmbeddedPage(href))
                {
                    httpCleanup();
                    //use default click handling
                    return;
                }

                // XXX_jblas: Ideally links to application pages should be specified as
                //            an url to the application document with a hash that is either
                //            the site relative path or id to the page. But some of the
                //            internal code that dynamically generates sub-pages for nested
                //            lists and select dialogs, just write a hash in the link they
                //            create. This means the actual URL path is based on whatever
                //            the current value of the base tag is at the time this code
                //            is called. For now we are just assuming that any url with a
                //            hash in it is an application page reference.
                if (href.search("#") !== -1)
                {
                    href = href.replace(/[^#]*#/, "");
                    if (!href)
                    {
                        //link was an empty hash meant purely
                        //for interaction, so we ignore it.
                        event.preventDefault();
                        return;
                    } else if (path.isPath(href))
                    {
                        //we have apath so make it the href we want to load.
                        href = path.makeUrlAbsolute(href, baseUrl);
                    } else
                    {
                        //we have a simple id so use the documentUrl as its base.
                        href = path.makeUrlAbsolute("#" + href, documentUrl.hrefNoHash);
                    }
                }

                // Should we handle this link, or let the browser deal with it?
                var useDefaultUrlHandling = $link.is("[rel='external']") || $link.is(":jqmData(ajax='false')") || $link.is("[target]"),

                // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
                // requests if the document doing the request was loaded via the file:// protocol.
                // This is usually to allow the application to "phone home" and fetch app specific
                // data. We normally let the browser handle external/cross-domain urls, but if the
                // allowCrossDomainPages option is true, we will allow cross-domain http/https
                // requests to go through our page loading logic.

                //check for protocol or rel and its not an embedded page
                //TODO overlap in logic from isExternal, rel=external check should be
                //     moved into more comprehensive isExternalLink
				isExternal = useDefaultUrlHandling || (path.isExternal(href) && !path.isPermittedCrossDomainRequest(documentUrl, href));

                if (isExternal)
                {
                    httpCleanup();
                    //use default click handling
                    return;
                }

                //use ajax
                var transition = $link.jqmData("transition"),
				reverse = $link.jqmData("direction") === "reverse" ||
                // deprecated - remove by 1.0
							$link.jqmData("back"),

                //this may need to be more specific as we use data-rel more
				role = $link.attr("data-" + $.mobile.ns + "rel") || undefined;

                if (role === "popup")
                {
                    $.mobile.popup.handleLink($link);
                }
                else
                {
                    $.mobile.changePage(href, { transition: transition, reverse: reverse, role: role });
                }
                event.preventDefault();
            });

            //prefetch pages when anchors with data-prefetch are encountered
            $(document).delegate(".ui-page", "pageshow.prefetch", function()
            {
                var urls = [];
                $(this).find("a:jqmData(prefetch)").each(function()
                {
                    var $link = $(this),
					url = $link.attr("href");

                    if (url && $.inArray(url, urls) === -1)
                    {
                        urls.push(url);

                        $.mobile.loadPage(url, { role: $link.attr("data-" + $.mobile.ns + "rel") });
                    }
                });
            });

            $.mobile._handleHashChange = function(hash)
            {
                //find first page via hash
                var to = path.stripHash(hash),
                //transition is false if it's the first page, undefined otherwise (and may be overridden by default)
				transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,

                // default options for the changPage calls made after examining the current state
                // of the page and the hash
				changePageOptions = {
				    transition: transition,
				    changeHash: false,
				    fromHashChange: true
				};

                if (0 === urlHistory.stack.length)
                {
                    urlHistory.initialDst = to;
                }

                //if listening is disabled (either globally or temporarily), or it's a dialog hash
                if (!$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange)
                {
                    urlHistory.ignoreNextHashChange = false;
                    return;
                }

                // special case for dialogs
                if (urlHistory.stack.length > 1 && to.indexOf(dialogHashKey) > -1 && urlHistory.initialDst !== to)
                {

                    // If current active page is not a dialog skip the dialog and continue
                    // in the same direction
                    if (!$.mobile.activePage.is(".ui-dialog"))
                    {
                        //determine if we're heading forward or backward and continue accordingly past
                        //the current dialog
                        urlHistory.directHashChange({
                            currentUrl: to,
                            isBack: function() { window.history.back(); },
                            isForward: function() { window.history.forward(); }
                        });

                        // prevent changePage()
                        return;
                    } else
                    {
                        // if the current active page is a dialog and we're navigating
                        // to a dialog use the dialog objected saved in the stack
                        urlHistory.directHashChange({
                            currentUrl: to,

                            // regardless of the direction of the history change
                            // do the following
                            either: function(isBack)
                            {
                                var active = $.mobile.urlHistory.getActive();

                                to = active.pageUrl;

                                // make sure to set the role, transition and reversal
                                // as most of this is lost by the domCache cleaning
                                $.extend(changePageOptions, {
                                    role: active.role,
                                    transition: active.transition,
                                    reverse: isBack
                                });
                            }
                        });
                    }
                }

                //if to is defined, load it
                if (to)
                {
                    // At this point, 'to' can be one of 3 things, a cached page element from
                    // a history stack entry, an id, or site-relative/absolute URL. If 'to' is
                    // an id, we need to resolve it against the documentBase, not the location.href,
                    // since the hashchange could've been the result of a forward/backward navigation
                    // that crosses from an external page/dialog to an internal page/dialog.
                    to = (typeof to === "string" && !path.isPath(to)) ? (path.makeUrlAbsolute('#' + to, documentBase)) : to;
                    $.mobile.changePage(to, changePageOptions);
                } else
                {
                    //there's no hash, go to the first page in the dom
                    $.mobile.changePage($.mobile.firstPage, changePageOptions);
                }
            };

            //hashchange event handler
            $window.bind("hashchange", function(e, triggered)
            {
                $.mobile._handleHashChange(location.hash);
            });

            //set page min-heights to be device specific
            $(document).bind("pageshow", resetActivePageHeight);
            $(window).bind("throttledresize", resetActivePageHeight);

        }); //navreadyDeferred done callback

    })(jQuery);

    (function($, window)
    {
        // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
        // Scope self to pushStateHandler so we can reference it sanely within the
        // methods handed off as event handlers
        var pushStateHandler = {},
		self = pushStateHandler,
		$win = $(window),
		url = $.mobile.path.parseUrl(location.href),
		mobileinitDeferred = $.Deferred(),
		domreadyDeferred = $.Deferred();

        $(document).ready($.proxy(domreadyDeferred, "resolve"));

        $(document).one("mobileinit", $.proxy(mobileinitDeferred, "resolve"));

        $.extend(pushStateHandler, {
            // TODO move to a path helper, this is rather common functionality
            initialFilePath: (function()
            {
                return url.pathname + url.search;
            })(),

            hashChangeTimeout: 200,

            hashChangeEnableTimer: undefined,

            initialHref: url.hrefNoHash,

            state: function()
            {
                return {
                    hash: location.hash || "#" + self.initialFilePath,
                    title: document.title,

                    // persist across refresh
                    initialHref: self.initialHref
                };
            },

            resetUIKeys: function(url)
            {
                var dialog = $.mobile.dialogHashKey,
				subkey = "&" + $.mobile.subPageUrlKey,
				dialogIndex = url.indexOf(dialog);

                if (dialogIndex > -1)
                {
                    url = url.slice(0, dialogIndex) + "#" + url.slice(dialogIndex);
                } else if (url.indexOf(subkey) > -1)
                {
                    url = url.split(subkey).join("#" + subkey);
                }

                return url;
            },

            // TODO sort out a single barrier to hashchange functionality
            nextHashChangePrevented: function(value)
            {
                $.mobile.urlHistory.ignoreNextHashChange = value;
                self.onHashChangeDisabled = value;
            },

            // on hash change we want to clean up the url
            // NOTE this takes place *after* the vanilla navigation hash change
            // handling has taken place and set the state of the DOM
            onHashChange: function(e)
            {
                // disable this hash change
                if (self.onHashChangeDisabled)
                {
                    return;
                }

                var href, state,
				hash = location.hash,
				isPath = $.mobile.path.isPath(hash),
				resolutionUrl = isPath ? location.href : $.mobile.getDocumentUrl();

                hash = isPath ? hash.replace("#", "") : hash;


                // propulate the hash when its not available
                state = self.state();

                // make the hash abolute with the current href
                href = $.mobile.path.makeUrlAbsolute(hash, resolutionUrl);

                if (isPath)
                {
                    href = self.resetUIKeys(href);
                }

                // replace the current url with the new href and store the state
                // Note that in some cases we might be replacing an url with the
                // same url. We do this anyways because we need to make sure that
                // all of our history entries have a state object associated with
                // them. This allows us to work around the case where window.history.back()
                // is called to transition from an external page to an embedded page.
                // In that particular case, a hashchange event is *NOT* generated by the browser.
                // Ensuring each history entry has a state object means that onPopState()
                // will always trigger our hashchange callback even when a hashchange event
                // is not fired.
                history.replaceState(state, document.title, href);
            },

            // on popstate (ie back or forward) we need to replace the hash that was there previously
            // cleaned up by the additional hash handling
            onPopState: function(e)
            {
                var poppedState = e.originalEvent.state,
				fromHash, toHash, hashChanged;

                // if there's no state its not a popstate we care about, eg chrome's initial popstate
                if (poppedState)
                {
                    // if we get two pop states in under this.hashChangeTimeout
                    // make sure to clear any timer set for the previous change
                    clearTimeout(self.hashChangeEnableTimer);

                    // make sure to enable hash handling for the the _handleHashChange call
                    self.nextHashChangePrevented(false);

                    // change the page based on the hash in the popped state
                    $.mobile._handleHashChange(poppedState.hash);

                    // prevent any hashchange in the next self.hashChangeTimeout
                    self.nextHashChangePrevented(true);

                    // re-enable hash change handling after swallowing a possible hash
                    // change event that comes on all popstates courtesy of browsers like Android
                    self.hashChangeEnableTimer = setTimeout(function()
                    {
                        self.nextHashChangePrevented(false);
                    }, self.hashChangeTimeout);
                }
            },

            init: function()
            {
                $win.bind("hashchange", self.onHashChange);

                // Handle popstate events the occur through history changes
                $win.bind("popstate", self.onPopState);

                // if there's no hash, we need to replacestate for returning to home
                if (location.hash === "")
                {
                    history.replaceState(self.state(), document.title, location.href);
                }
            }
        });

        // We need to init when "mobileinit", "domready", and "navready" have all happened
        $.when(domreadyDeferred, mobileinitDeferred, $.mobile.navreadyDeferred).done(function()
        {
            if ($.mobile.pushStateEnabled && $.support.pushState)
            {
                pushStateHandler.init();
            }
        });
    })(jQuery, this);

    /*
    * fallback transition for flip in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        $.mobile.transitionFallbacks.flip = "fade";

    })(jQuery, this);
    /*
    * fallback transition for flow in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        $.mobile.transitionFallbacks.flow = "fade";

    })(jQuery, this);
    /*
    * fallback transition for pop in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        $.mobile.transitionFallbacks.pop = "fade";

    })(jQuery, this);
    /*
    * fallback transition for slide in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        // Use the simultaneous transitions handler for slide transitions
        $.mobile.transitionHandlers.slide = $.mobile.transitionHandlers.simultaneous;

        // Set the slide transitions's fallback to "fade"
        $.mobile.transitionFallbacks.slide = "fade";

    })(jQuery, this);
    /*
    * fallback transition for slidedown in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        $.mobile.transitionFallbacks.slidedown = "fade";

    })(jQuery, this);
    /*
    * fallback transition for slidefade in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        // Set the slide transitions's fallback to "fade"
        $.mobile.transitionFallbacks.slidefade = "fade";

    })(jQuery, this);
    /*
    * fallback transition for slideup in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        $.mobile.transitionFallbacks.slideup = "fade";

    })(jQuery, this);
    /*
    * fallback transition for turn in non-3D supporting browsers (which tend to handle complex transitions poorly in general
    */

    (function($, window, undefined)
    {

        $.mobile.transitionFallbacks.turn = "fade";

    })(jQuery, this);

    (function($, undefined)
    {

        $.mobile.page.prototype.options.degradeInputs = {
            color: false,
            date: false,
            datetime: false,
            "datetime-local": false,
            email: false,
            month: false,
            number: false,
            range: "number",
            search: "text",
            tel: false,
            time: false,
            url: false,
            week: false
        };


        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {

            var page = $.mobile.closestPageData($(e.target)), options;

            if (!page)
            {
                return;
            }

            options = page.options;

            // degrade inputs to avoid poorly implemented native functionality
            $(e.target).find("input").not(page.keepNativeSelector()).each(function()
            {
                var $this = $(this),
			type = this.getAttribute("type"),
			optType = options.degradeInputs[type] || "text";

                if (options.degradeInputs[type])
                {
                    var html = $("<div>").html($this.clone()).html(),
                    // In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead
				hasType = html.indexOf(" type=") > -1,
				findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/,
				repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + (hasType ? "" : ">");

                    $this.replaceWith(html.replace(findstr, repstr));
                }
            });

        });

    })(jQuery);

    (function($, window, undefined)
    {

        $.widget("mobile.dialog", $.mobile.widget, {
            options: {
                closeBtnText: "Close",
                overlayTheme: "a",
                initSelector: ":jqmData(role='dialog')"
            },
            _create: function()
            {
                var self = this,
			$el = this.element,
			headerCloseButton = $("<a href='#' data-" + $.mobile.ns + "icon='delete' data-" + $.mobile.ns + "iconpos='notext'>" + this.options.closeBtnText + "</a>"),
			dialogWrap = $("<div/>", {
			    "role": "dialog",
			    "class": "ui-dialog-contain ui-corner-all ui-overlay-shadow"
			});

                $el.addClass("ui-dialog ui-overlay-" + this.options.overlayTheme);

                // Class the markup for dialog styling
                // Set aria role
                $el
			.wrapInner(dialogWrap)
			.children()
				.find(":jqmData(role='header')")
					.prepend(headerCloseButton)
				.end()
				.children(':first-child')
					.addClass("ui-corner-top")
				.end()
				.children(":last-child")
					.addClass("ui-corner-bottom");

                // this must be an anonymous function so that select menu dialogs can replace
                // the close method. This is a change from previously just defining data-rel=back
                // on the button and letting nav handle it
                //
                // Use click rather than vclick in order to prevent the possibility of unintentionally
                // reopening the dialog if the dialog opening item was directly under the close button.
                headerCloseButton.bind("click", function()
                {
                    self.close();
                });

                /* bind events
                - clicks and submits should use the closing transition that the dialog opened with
                unless a data-transition is specified on the link/form
                - if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally
                */
                $el.bind("vclick submit", function(event)
                {
                    var $target = $(event.target).closest(event.type === "vclick" ? "a" : "form"),
				active;

                    if ($target.length && !$target.jqmData("transition"))
                    {

                        active = $.mobile.urlHistory.getActive() || {};

                        $target.attr("data-" + $.mobile.ns + "transition", (active.transition || $.mobile.defaultDialogTransition))
					.attr("data-" + $.mobile.ns + "direction", "reverse");
                    }
                })
		.bind("pagehide", function(e, ui)
		{
		    self._isClosed = false;
		    $(this).find("." + $.mobile.activeBtnClass).not(".ui-slider-bg").removeClass($.mobile.activeBtnClass);
		})
                // Override the theme set by the page plugin on pageshow
		.bind("pagebeforeshow", function()
		{
		    if (self.options.overlayTheme)
		    {
		        self.element
					.page("removeContainerBackground")
					.page("setContainerBackground", self.options.overlayTheme);
		    }
		});
            },

            // Close method goes back in history
            close: function()
            {
                if (!this._isClosed)
                {
                    this._isClosed = true;
                    if ($.mobile.hashListeningEnabled)
                    {
                        window.history.back();
                    }
                    else
                    {
                        $.mobile.changePage($.mobile.urlHistory.getPrev().url);
                    }
                }
            }
        });

        //auto self-init widgets
        $(document).delegate($.mobile.dialog.prototype.options.initSelector, "pagecreate", function()
        {
            $.mobile.dialog.prototype.enhance(this);
        });

    })(jQuery, this);

    (function($, undefined)
    {

        $.mobile.page.prototype.options.backBtnText = "Back";
        $.mobile.page.prototype.options.addBackBtn = false;
        $.mobile.page.prototype.options.backBtnTheme = null;
        $.mobile.page.prototype.options.headerTheme = "a";
        $.mobile.page.prototype.options.footerTheme = "a";
        $.mobile.page.prototype.options.contentTheme = null;

        // NOTE bind used to force this binding to run before the buttonMarkup binding
        //      which expects .ui-footer top be applied in its gigantic selector
        // TODO remove the buttonMarkup giant selector and move it to the various modules
        //      on which it depends
        $(document).bind("pagecreate", function(e)
        {
            var $page = $(e.target),
		o = $page.data("page").options,
		pageRole = $page.jqmData("role"),
		pageTheme = o.theme;

            $(":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", $page)
		.jqmEnhanceable()
		.each(function()
		{

		    var $this = $(this),
			role = $this.jqmData("role"),
			theme = $this.jqmData("theme"),
			contentTheme = theme || o.contentTheme || (pageRole === "dialog" && pageTheme),
			$headeranchors,
			leftbtn,
			rightbtn,
			backBtn;

		    $this.addClass("ui-" + role);

		    //apply theming and markup modifications to page,header,content,footer
		    if (role === "header" || role === "footer")
		    {

		        var thisTheme = theme || (role === "header" ? o.headerTheme : o.footerTheme) || pageTheme;

		        $this
		        //add theme class
				.addClass("ui-bar-" + thisTheme)
		        // Add ARIA role
				.attr("role", role === "header" ? "banner" : "contentinfo");

		        if (role === "header")
		        {
		            // Right,left buttons
		            $headeranchors = $this.children("a");
		            leftbtn = $headeranchors.hasClass("ui-btn-left");
		            rightbtn = $headeranchors.hasClass("ui-btn-right");

		            leftbtn = leftbtn || $headeranchors.eq(0).not(".ui-btn-right").addClass("ui-btn-left").length;

		            rightbtn = rightbtn || $headeranchors.eq(1).addClass("ui-btn-right").length;
		        }

		        // Auto-add back btn on pages beyond first view
		        if (o.addBackBtn &&
				role === "header" &&
				$(".ui-page").length > 1 &&
				$page.jqmData("url") !== $.mobile.path.stripHash(location.hash) &&
				!leftbtn)
		        {

		            backBtn = $("<a href='javascript:void(0);' class='ui-btn-left' data-" + $.mobile.ns + "rel='back' data-" + $.mobile.ns + "icon='arrow-l'>" + o.backBtnText + "</a>")
		            // If theme is provided, override default inheritance
					.attr("data-" + $.mobile.ns + "theme", o.backBtnTheme || thisTheme)
					.prependTo($this);
		        }

		        // Page title
		        $this.children("h1, h2, h3, h4, h5, h6")
				.addClass("ui-title")
		        // Regardless of h element number in src, it becomes h1 for the enhanced page
				.attr({
				    "role": "heading",
				    "aria-level": "1"
				});

		    } else if (role === "content")
		    {
		        if (contentTheme)
		        {
		            $this.addClass("ui-body-" + (contentTheme));
		        }

		        // Add ARIA role
		        $this.attr("role", "main");
		    }
		});
        });

    })(jQuery);

    (function($, undefined)
    {

        // filter function removes whitespace between label and form element so we can use inline-block (nodeType 3 = text)
        $.fn.fieldcontain = function(options)
        {
            return this
		.addClass("ui-field-contain ui-body ui-br")
		.contents().filter(function()
		{
		    return (this.nodeType === 3 && !/\S/.test(this.nodeValue));
		}).remove();
        };

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $(":jqmData(role='fieldcontain')", e.target).jqmEnhanceable().fieldcontain();
        });

    })(jQuery);

    (function($, undefined)
    {

        $.fn.grid = function(options)
        {
            return this.each(function()
            {

                var $this = $(this),
			o = $.extend({
			    grid: null
			}, options),
			$kids = $this.children(),
			gridCols = { solo: 1, a: 2, b: 3, c: 4, d: 5 },
			grid = o.grid,
			iterator;

                if (!grid)
                {
                    if ($kids.length <= 5)
                    {
                        for (var letter in gridCols)
                        {
                            if (gridCols[letter] === $kids.length)
                            {
                                grid = letter;
                            }
                        }
                    } else
                    {
                        grid = "a";
                        $this.addClass("ui-grid-duo");
                    }
                }
                iterator = gridCols[grid];

                $this.addClass("ui-grid-" + grid);

                $kids.filter(":nth-child(" + iterator + "n+1)").addClass("ui-block-a");

                if (iterator > 1)
                {
                    $kids.filter(":nth-child(" + iterator + "n+2)").addClass("ui-block-b");
                }
                if (iterator > 2)
                {
                    $kids.filter(":nth-child(3n+3)").addClass("ui-block-c");
                }
                if (iterator > 3)
                {
                    $kids.filter(":nth-child(4n+4)").addClass("ui-block-d");
                }
                if (iterator > 4)
                {
                    $kids.filter(":nth-child(5n+5)").addClass("ui-block-e");
                }
            });
        };
    })(jQuery);

    (function($, undefined)
    {

        $(document).bind("pagecreate create", function(e)
        {
            $(":jqmData(role='nojs')", e.target).addClass("ui-nojs");

        });

    })(jQuery);

    (function($, undefined)
    {

        $.fn.buttonMarkup = function(options)
        {
            var $workingSet = this,
		mapToDataAttr = function(key, value)
		{
		    e.setAttribute("data-" + $.mobile.ns + key, value);
		    el.jqmData(key, value);
		};

            // Enforce options to be of type string
            options = (options && ($.type(options) === "object")) ? options : {};
            for (var i = 0; i < $workingSet.length; i++)
            {
                var el = $workingSet.eq(i),
			e = el[0],
			o = $.extend({}, $.fn.buttonMarkup.defaults, {
			    icon: options.icon !== undefined ? options.icon : el.jqmData("icon"),
			    iconpos: options.iconpos !== undefined ? options.iconpos : el.jqmData("iconpos"),
			    theme: options.theme !== undefined ? options.theme : el.jqmData("theme") || $.mobile.getInheritedTheme(el, "c"),
			    inline: options.inline !== undefined ? options.inline : el.jqmData("inline"),
			    shadow: options.shadow !== undefined ? options.shadow : el.jqmData("shadow"),
			    corners: options.corners !== undefined ? options.corners : el.jqmData("corners"),
			    iconshadow: options.iconshadow !== undefined ? options.iconshadow : el.jqmData("iconshadow"),
			    mini: options.mini !== undefined ? options.mini : el.jqmData("mini")
			}, options),

                // Classes Defined
			innerClass = "ui-btn-inner",
			textClass = "ui-btn-text",
			buttonClass, iconClass,
                // Button inner markup
			buttonInner,
			buttonText,
			buttonIcon,
			buttonElements;

                $.each(o, mapToDataAttr);

                if (el.jqmData("rel") === "popup" && el.attr("href"))
                {
                    e.setAttribute("aria-haspopup", true);
                    e.setAttribute("aria-owns", e.getAttribute("href"));
                }

                // Check if this element is already enhanced
                buttonElements = $.data(((e.tagName === "INPUT" || e.tagName === "BUTTON") ? e.parentNode : e), "buttonElements");

                if (buttonElements)
                {
                    e = buttonElements.outer;
                    el = $(e);
                    buttonInner = buttonElements.inner;
                    buttonText = buttonElements.text;
                    // We will recreate this icon below
                    $(buttonElements.icon).remove();
                    buttonElements.icon = null;
                }
                else
                {
                    buttonInner = document.createElement(o.wrapperEls);
                    buttonText = document.createElement(o.wrapperEls);
                }
                buttonIcon = o.icon ? document.createElement("span") : null;

                if (attachEvents && !buttonElements)
                {
                    attachEvents();
                }

                // if not, try to find closest theme container
                if (!o.theme)
                {
                    o.theme = $.mobile.getInheritedTheme(el, "c");
                }

                buttonClass = "ui-btn ui-btn-up-" + o.theme;
                buttonClass += o.shadow ? " ui-shadow" : "";
                buttonClass += o.corners ? " ui-btn-corner-all" : "";

                if (o.mini !== undefined)
                {
                    // Used to control styling in headers/footers, where buttons default to `mini` style.
                    buttonClass += o.mini === true ? " ui-mini" : " ui-fullsize";
                }

                if (o.inline !== undefined)
                {
                    // Used to control styling in headers/footers, where buttons default to `inline` style.
                    buttonClass += o.inline === true ? " ui-btn-inline" : " ui-btn-block";
                }

                if (o.icon)
                {
                    o.icon = "ui-icon-" + o.icon;
                    o.iconpos = o.iconpos || "left";

                    iconClass = "ui-icon " + o.icon;

                    if (o.iconshadow)
                    {
                        iconClass += " ui-icon-shadow";
                    }
                }

                if (o.iconpos)
                {
                    buttonClass += " ui-btn-icon-" + o.iconpos;

                    if (o.iconpos === "notext" && !el.attr("title"))
                    {
                        el.attr("title", el.getEncodedText());
                    }
                }

                innerClass += o.corners ? " ui-btn-corner-all" : "";

                if (o.iconpos && o.iconpos === "notext" && !el.attr("title"))
                {
                    el.attr("title", el.getEncodedText());
                }

                if (buttonElements)
                {
                    el.removeClass(buttonElements.bcls || "");
                }
                el.removeClass("ui-link").addClass(buttonClass);

                buttonInner.className = innerClass;

                buttonText.className = textClass;
                if (!buttonElements)
                {
                    buttonInner.appendChild(buttonText);
                }
                if (buttonIcon)
                {
                    buttonIcon.className = iconClass;
                    if (!(buttonElements && buttonElements.icon))
                    {
                        buttonIcon.appendChild(document.createTextNode("\u00a0"));
                        buttonInner.appendChild(buttonIcon);
                    }
                }

                while (e.firstChild && !buttonElements)
                {
                    buttonText.appendChild(e.firstChild);
                }

                if (!buttonElements)
                {
                    e.appendChild(buttonInner);
                }

                // Assign a structure containing the elements of this button to the elements of this button. This
                // will allow us to recognize this as an already-enhanced button in future calls to buttonMarkup().
                buttonElements = {
                    bcls: buttonClass,
                    outer: e,
                    inner: buttonInner,
                    text: buttonText,
                    icon: buttonIcon
                };

                $.data(e, 'buttonElements', buttonElements);
                $.data(buttonInner, 'buttonElements', buttonElements);
                $.data(buttonText, 'buttonElements', buttonElements);
                if (buttonIcon)
                {
                    $.data(buttonIcon, 'buttonElements', buttonElements);
                }
            }

            return this;
        };

        $.fn.buttonMarkup.defaults = {
            corners: true,
            shadow: true,
            iconshadow: true,
            wrapperEls: "span"
        };

        function closestEnabledButton(element)
        {
            var cname;

            while (element)
            {
                // Note that we check for typeof className below because the element we
                // handed could be in an SVG DOM where className on SVG elements is defined to
                // be of a different type (SVGAnimatedString). We only operate on HTML DOM
                // elements, so we look for plain "string".
                cname = (typeof element.className === 'string') && (element.className + ' ');
                if (cname && cname.indexOf("ui-btn ") > -1 && cname.indexOf("ui-disabled ") < 0)
                {
                    break;
                }

                element = element.parentNode;
            }

            return element;
        }

        var attachEvents = function()
        {
            var hoverDelay = $.mobile.buttonMarkup.hoverDelay, hov, foc;

            $(document).bind({
                "vmousedown vmousecancel vmouseup vmouseover vmouseout focus blur scrollstart": function(event)
                {
                    var theme,
				$btn = $(closestEnabledButton(event.target)),
				isTouchEvent = event.originalEvent && /^touch/.test(event.originalEvent.type),
				evt = event.type;

                    if ($btn.length)
                    {
                        theme = $btn.attr("data-" + $.mobile.ns + "theme");

                        if (evt === "vmousedown")
                        {
                            if (isTouchEvent)
                            {
                                // Use a short delay to determine if the user is scrolling before highlighting
                                hov = setTimeout(function()
                                {
                                    $btn.removeClass("ui-btn-up-" + theme).addClass("ui-btn-down-" + theme);
                                }, hoverDelay);
                            } else
                            {
                                $btn.removeClass("ui-btn-up-" + theme).addClass("ui-btn-down-" + theme);
                            }
                        } else if (evt === "vmousecancel" || evt === "vmouseup")
                        {
                            $btn.removeClass("ui-btn-down-" + theme).addClass("ui-btn-up-" + theme);
                        } else if (evt === "vmouseover" || evt === "focus")
                        {
                            if (isTouchEvent)
                            {
                                // Use a short delay to determine if the user is scrolling before highlighting
                                foc = setTimeout(function()
                                {
                                    $btn.removeClass("ui-btn-up-" + theme).addClass("ui-btn-hover-" + theme);
                                }, hoverDelay);
                            } else
                            {
                                $btn.removeClass("ui-btn-up-" + theme).addClass("ui-btn-hover-" + theme);
                            }
                        } else if (evt === "vmouseout" || evt === "blur" || evt === "scrollstart")
                        {
                            $btn.removeClass("ui-btn-hover-" + theme + " ui-btn-down-" + theme).addClass("ui-btn-up-" + theme);
                            if (hov)
                            {
                                clearTimeout(hov);
                            }
                            if (foc)
                            {
                                clearTimeout(foc);
                            }
                        }
                    }
                },
                "focusin focus": function(event)
                {
                    $(closestEnabledButton(event.target)).addClass($.mobile.focusClass);
                },
                "focusout blur": function(event)
                {
                    $(closestEnabledButton(event.target)).removeClass($.mobile.focusClass);
                }
            });

            attachEvents = null;
        };

        //links in bars, or those with  data-role become buttons
        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {

            $(":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a", e.target)
		.not("button, input, .ui-btn, :jqmData(role='none'), :jqmData(role='nojs')")
		.buttonMarkup();
        });

    })(jQuery);


    (function($, undefined)
    {

        $.widget("mobile.collapsible", $.mobile.widget, {
            options: {
                expandCueText: " click to expand contents",
                collapseCueText: " click to collapse contents",
                collapsed: true,
                heading: "h1,h2,h3,h4,h5,h6,legend",
                theme: null,
                contentTheme: null,
                mini: false,
                initSelector: ":jqmData(role='collapsible')"
            },
            _create: function()
            {

                var $el = this.element,
			o = this.options,
			collapsible = $el.addClass("ui-collapsible"),
			collapsibleHeading = $el.children(o.heading).first(),
			collapsedIcon = $el.jqmData("collapsed-icon") || o.collapsedIcon,
			expandedIcon = $el.jqmData("expanded-icon") || o.expandedIcon,
			collapsibleContent = collapsible.wrapInner("<div class='ui-collapsible-content'></div>").children(".ui-collapsible-content"),
			collapsibleSet = $el.closest(":jqmData(role='collapsible-set')").addClass("ui-collapsible-set");

                // Replace collapsibleHeading if it's a legend
                if (collapsibleHeading.is("legend"))
                {
                    collapsibleHeading = $("<div role='heading'>" + collapsibleHeading.html() + "</div>").insertBefore(collapsibleHeading);
                    collapsibleHeading.next().remove();
                }

                // If we are in a collapsible set
                if (collapsibleSet.length)
                {
                    // Inherit the theme from collapsible-set
                    if (!o.theme)
                    {
                        o.theme = collapsibleSet.jqmData("theme") || $.mobile.getInheritedTheme(collapsibleSet, "c");
                    }
                    // Inherit the content-theme from collapsible-set
                    if (!o.contentTheme)
                    {
                        o.contentTheme = collapsibleSet.jqmData("content-theme");
                    }

                    // Get the preference for collapsed icon in the set
                    if (!o.collapsedIcon)
                    {
                        o.collapsedIcon = collapsibleSet.jqmData("collapsed-icon");
                    }
                    // Get the preference for expanded icon in the set
                    if (!o.expandedIcon)
                    {
                        o.expandedIcon = collapsibleSet.jqmData("expanded-icon");
                    }
                    // Gets the preference icon position in the set
                    if (!o.iconPos)
                    {
                        o.iconPos = collapsibleSet.jqmData("iconpos");
                    }

                    if (!o.mini)
                    {
                        o.mini = collapsibleSet.jqmData("mini");
                    }
                }
                collapsibleContent.addClass((o.contentTheme) ? ("ui-body-" + o.contentTheme) : "");

                collapsedIcon = $el.jqmData("collapsed-icon") || o.collapsedIcon || "plus";
                expandedIcon = $el.jqmData("expanded-icon") || o.expandedIcon || "minus";

                collapsibleHeading
                //drop heading in before content
			.insertBefore(collapsibleContent)
                //modify markup & attributes
			.addClass("ui-collapsible-heading")
			.append("<span class='ui-collapsible-heading-status'></span>")
			.wrapInner("<a href='#' class='ui-collapsible-heading-toggle'></a>")
			.find("a")
				.first()
				.buttonMarkup({
				    shadow: false,
				    corners: false,
				    iconpos: $el.jqmData("iconpos") || o.iconPos || "left",
				    icon: collapsedIcon,
				    mini: o.mini,
				    theme: o.theme
				})
			.add(".ui-btn-inner", $el)
				.addClass("ui-corner-top ui-corner-bottom");

                //events
                collapsible
			.bind("expand collapse", function(event)
			{
			    if (!event.isDefaultPrevented())
			    {
			        var $this = $(this),
						isCollapse = (event.type === "collapse"),
						contentTheme = o.contentTheme;

			        event.preventDefault();

			        collapsibleHeading
						.toggleClass("ui-collapsible-heading-collapsed", isCollapse)
						.find(".ui-collapsible-heading-status")
							.text(isCollapse ? o.expandCueText : o.collapseCueText)
						.end()
						.find(".ui-icon")
							.toggleClass("ui-icon-" + expandedIcon, !isCollapse)
							.toggleClass("ui-icon-" + collapsedIcon, isCollapse)
						.end()
						.find("a").first().removeClass($.mobile.activeBtnClass);

			        $this.toggleClass("ui-collapsible-collapsed", isCollapse);
			        collapsibleContent.toggleClass("ui-collapsible-content-collapsed", isCollapse).attr("aria-hidden", isCollapse);

			        if (contentTheme && (!collapsibleSet.length || collapsible.jqmData("collapsible-last")))
			        {
			            collapsibleHeading
							.find("a").first().add(collapsibleHeading.find(".ui-btn-inner"))
							.toggleClass("ui-corner-bottom", isCollapse);
			            collapsibleContent.toggleClass("ui-corner-bottom", !isCollapse);
			        }
			        collapsibleContent.trigger("updatelayout");
			    }
			})
			.trigger(o.collapsed ? "collapse" : "expand");

                collapsibleHeading
			.bind("tap", function(event)
			{
			    collapsibleHeading.find("a").first().addClass($.mobile.activeBtnClass);
			})
			.bind("click", function(event)
			{

			    var type = collapsibleHeading.is(".ui-collapsible-heading-collapsed") ?
										"expand" : "collapse";

			    collapsible.trigger(type);

			    event.preventDefault();
			    event.stopPropagation();
			});
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.collapsible.prototype.enhanceWithin(e.target);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.widget("mobile.collapsibleset", $.mobile.widget, {
            options: {
                initSelector: ":jqmData(role='collapsible-set')"
            },
            _create: function()
            {
                var $el = this.element.addClass("ui-collapsible-set"),
			o = this.options;

                // Inherit the theme from collapsible-set
                if (!o.theme)
                {
                    o.theme = $.mobile.getInheritedTheme($el, "c");
                }
                // Inherit the content-theme from collapsible-set
                if (!o.contentTheme)
                {
                    o.contentTheme = $el.jqmData("content-theme");
                }

                if (!o.corners)
                {
                    o.corners = $el.jqmData("corners") === undefined ? true : false;
                }

                // Initialize the collapsible set if it's not already initialized
                if (!$el.jqmData("collapsiblebound"))
                {
                    $el
				.jqmData("collapsiblebound", true)
				.bind("expand collapse", function(event)
				{
				    var isCollapse = (event.type === "collapse"),
						collapsible = $(event.target).closest(".ui-collapsible"),
						widget = collapsible.data("collapsible");
				    if (collapsible.jqmData("collapsible-last"))
				    {
				        collapsible.find(widget.options.heading).first()
							.find("a").first()
							.toggleClass("ui-corner-bottom", isCollapse)
							.find(".ui-btn-inner")
							.toggleClass("ui-corner-bottom", isCollapse);
				        collapsible.find(".ui-collapsible-content").toggleClass("ui-corner-bottom", !isCollapse);
				    }
				})
				.bind("expand", function(event)
				{
				    var closestCollapsible = $(event.target)
						.closest(".ui-collapsible");
				    if (closestCollapsible.parent().is(":jqmData(role='collapsible-set')"))
				    {
				        closestCollapsible
							.siblings(".ui-collapsible")
							.trigger("collapse");
				    }
				});
                }
            },

            _init: function()
            {
                var $el = this.element,
			collapsiblesInSet = $el.children(":jqmData(role='collapsible')"),
			expanded = collapsiblesInSet.filter(":jqmData(collapsed='false')");
                this.refresh();

                // Because the corners are handled by the collapsible itself and the default state is collapsed
                // That was causing https://github.com/jquery/jquery-mobile/issues/4116
                expanded.trigger("expand");
            },

            refresh: function()
            {
                var $el = this.element,
			o = this.options,
			collapsiblesInSet = $el.children(":jqmData(role='collapsible')");

                $.mobile.collapsible.prototype.enhance(collapsiblesInSet.not(".ui-collapsible"));

                // clean up borders
                collapsiblesInSet.each(function()
                {
                    $(this).find($.mobile.collapsible.prototype.options.heading)
				.find("a").first()
				.removeClass("ui-corner-top ui-corner-bottom")
				.find(".ui-btn-inner")
				.removeClass("ui-corner-top ui-corner-bottom");
                });

                collapsiblesInSet.first()
			.find("a")
				.first()
				.addClass(o.corners ? "ui-corner-top" : "")
				.find(".ui-btn-inner")
					.addClass("ui-corner-top");

                collapsiblesInSet.last()
			.jqmData("collapsible-last", true)
			.find("a")
				.first()
				.addClass(o.corners ? "ui-corner-bottom" : "")
				.find(".ui-btn-inner")
					.addClass("ui-corner-bottom");
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.collapsibleset.prototype.enhanceWithin(e.target);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.widget("mobile.navbar", $.mobile.widget, {
            options: {
                iconpos: "top",
                grid: null,
                initSelector: ":jqmData(role='navbar')"
            },

            _create: function()
            {

                var $navbar = this.element,
			$navbtns = $navbar.find("a"),
			iconpos = $navbtns.filter(":jqmData(icon)").length ?
									this.options.iconpos : undefined;

                $navbar.addClass("ui-navbar ui-mini")
			.attr("role", "navigation")
			.find("ul")
			.jqmEnhanceable()
			.grid({ grid: this.options.grid });

                $navbtns.buttonMarkup({
                    corners: false,
                    shadow: false,
                    inline: true,
                    iconpos: iconpos
                });

                $navbar.delegate("a", "vclick", function(event)
                {
                    if (!$(event.target).hasClass("ui-disabled"))
                    {
                        $navbtns.removeClass($.mobile.activeBtnClass);
                        $(this).addClass($.mobile.activeBtnClass);
                    }
                });

                // Buttons in the navbar with ui-state-persist class should regain their active state before page show
                $navbar.closest(".ui-page").bind("pagebeforeshow", function()
                {
                    $navbtns.filter(".ui-state-persist").addClass($.mobile.activeBtnClass);
                });
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.navbar.prototype.enhanceWithin(e.target);
        });

    })(jQuery);

    (function($, undefined)
    {

        //Keeps track of the number of lists per page UID
        //This allows support for multiple nested list in the same page
        //https://github.com/jquery/jquery-mobile/issues/1617
        var listCountPerPage = {};

        $.widget("mobile.listview", $.mobile.widget, {

            options: {
                theme: null,
                countTheme: "c",
                headerTheme: "b",
                dividerTheme: "b",
                splitIcon: "arrow-r",
                splitTheme: "b",
                inset: false,
                initSelector: ":jqmData(role='listview')"
            },

            _create: function()
            {
                var t = this,
			listviewClasses = "";

                listviewClasses += t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "";

                // create listview markup
                t.element.addClass(function(i, orig)
                {
                    return orig + " ui-listview " + listviewClasses;
                });

                t.refresh(true);
            },

            _removeCorners: function(li, which)
            {
                var top = "ui-corner-top ui-corner-tr ui-corner-tl",
			bot = "ui-corner-bottom ui-corner-br ui-corner-bl";

                li = li.add(li.find(".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb"));

                if (which === "top")
                {
                    li.removeClass(top);
                } else if (which === "bottom")
                {
                    li.removeClass(bot);
                } else
                {
                    li.removeClass(top + " " + bot);
                }
            },

            _refreshCorners: function(create)
            {
                var $li,
			$visibleli,
			$topli,
			$bottomli;

                if (this.options.inset)
                {
                    $li = this.element.children("li");
                    // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
                    $visibleli = create ? $li.not(".ui-screen-hidden") : $li.filter(":visible");

                    this._removeCorners($li);

                    // Select the first visible li element
                    $topli = $visibleli.first()
				.addClass("ui-corner-top");

                    $topli.add($topli.find(".ui-btn-inner")
					.not(".ui-li-link-alt span:first-child"))
                                .addClass("ui-corner-top")
                                .end()
				.find(".ui-li-link-alt, .ui-li-link-alt span:first-child")
					.addClass("ui-corner-tr")
				.end()
				.find(".ui-li-thumb")
					.not(".ui-li-icon")
					.addClass("ui-corner-tl");

                    // Select the last visible li element
                    $bottomli = $visibleli.last()
				.addClass("ui-corner-bottom");

                    $bottomli.add($bottomli.find(".ui-btn-inner"))
				.find(".ui-li-link-alt")
					.addClass("ui-corner-br")
				.end()
				.find(".ui-li-thumb")
					.not(".ui-li-icon")
					.addClass("ui-corner-bl");
                }
                if (!create)
                {
                    this.element.trigger("updatelayout");
                }
            },

            // This is a generic utility method for finding the first
            // node with a given nodeName. It uses basic DOM traversal
            // to be fast and is meant to be a substitute for simple
            // $.fn.closest() and $.fn.children() calls on a single
            // element. Note that callers must pass both the lowerCase
            // and upperCase version of the nodeName they are looking for.
            // The main reason for this is that this function will be
            // called many times and we want to avoid having to lowercase
            // the nodeName from the element every time to ensure we have
            // a match. Note that this function lives here for now, but may
            // be moved into $.mobile if other components need a similar method.
            _findFirstElementByTagName: function(ele, nextProp, lcName, ucName)
            {
                var dict = {};
                dict[lcName] = dict[ucName] = true;
                while (ele)
                {
                    if (dict[ele.nodeName])
                    {
                        return ele;
                    }
                    ele = ele[nextProp];
                }
                return null;
            },
            _getChildrenByTagName: function(ele, lcName, ucName)
            {
                var results = [],
			dict = {};
                dict[lcName] = dict[ucName] = true;
                ele = ele.firstChild;
                while (ele)
                {
                    if (dict[ele.nodeName])
                    {
                        results.push(ele);
                    }
                    ele = ele.nextSibling;
                }
                return $(results);
            },

            _addThumbClasses: function(containers)
            {
                var i, img, len = containers.length;
                for (i = 0; i < len; i++)
                {
                    img = $(this._findFirstElementByTagName(containers[i].firstChild, "nextSibling", "img", "IMG"));
                    if (img.length)
                    {
                        img.addClass("ui-li-thumb");
                        $(this._findFirstElementByTagName(img[0].parentNode, "parentNode", "li", "LI")).addClass(img.is(".ui-li-icon") ? "ui-li-has-icon" : "ui-li-has-thumb");
                    }
                }
            },

            refresh: function(create)
            {
                this.parentPage = this.element.closest(".ui-page");
                this._createSubPages();

                var o = this.options,
			$list = this.element,
			self = this,
			dividertheme = $list.jqmData("dividertheme") || o.dividerTheme,
			listsplittheme = $list.jqmData("splittheme"),
			listspliticon = $list.jqmData("spliticon"),
			li = this._getChildrenByTagName($list[0], "li", "LI"),
			counter = $.support.cssPseudoElement || !$.nodeName($list[0], "ol") ? 0 : 1,
			itemClassDict = {},
			item, itemClass, itemTheme,
			a, last, splittheme, countParent, icon, imgParents, img, linkIcon;

                if (counter)
                {
                    $list.find(".ui-li-dec").remove();
                }

                if (!o.theme)
                {
                    o.theme = $.mobile.getInheritedTheme(this.element, "c");
                }

                for (var pos = 0, numli = li.length; pos < numli; pos++)
                {
                    item = li.eq(pos);
                    itemClass = "ui-li";

                    // If we're creating the element, we update it regardless
                    if (create || !item.hasClass("ui-li"))
                    {
                        itemTheme = item.jqmData("theme") || o.theme;
                        a = this._getChildrenByTagName(item[0], "a", "A");
                        var isDivider = (item.jqmData("role") === "list-divider");

                        if (a.length && !isDivider)
                        {
                            icon = item.jqmData("icon");

                            item.buttonMarkup({
                                wrapperEls: "div",
                                shadow: false,
                                corners: false,
                                iconpos: "right",
                                icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
                                theme: itemTheme
                            });

                            if ((icon !== false) && (a.length === 1))
                            {
                                item.addClass("ui-li-has-arrow");
                            }

                            a.first().removeClass("ui-link").addClass("ui-link-inherit");

                            if (a.length > 1)
                            {
                                itemClass += " ui-li-has-alt";

                                last = a.last();
                                splittheme = listsplittheme || last.jqmData("theme") || o.splitTheme;
                                linkIcon = last.jqmData("icon");

                                last.appendTo(item)
							.attr("title", last.getEncodedText())
							.addClass("ui-li-link-alt")
							.empty()
							.buttonMarkup({
							    shadow: false,
							    corners: false,
							    theme: itemTheme,
							    icon: false,
							    iconpos: "notext"
							})
							.find(".ui-btn-inner")
								.append(
									$(document.createElement("span")).buttonMarkup({
									    shadow: true,
									    corners: true,
									    theme: splittheme,
									    iconpos: "notext",
									    // link icon overrides list item icon overrides ul element overrides options
									    icon: linkIcon || icon || listspliticon || o.splitIcon
									})
								);
                            }
                        } else if (isDivider)
                        {

                            itemClass += " ui-li-divider ui-bar-" + dividertheme;
                            item.attr("role", "heading");

                            //reset counter when a divider heading is encountered
                            if (counter)
                            {
                                counter = 1;
                            }

                        } else
                        {
                            itemClass += " ui-li-static ui-btn-up-" + itemTheme;
                        }
                    }

                    if (counter && itemClass.indexOf("ui-li-divider") < 0)
                    {
                        countParent = item.is(".ui-li-static:first") ? item : item.find(".ui-link-inherit");

                        countParent.addClass("ui-li-jsnumbering")
					.prepend("<span class='ui-li-dec'>" + (counter++) + ". </span>");
                    }

                    // Instead of setting item class directly on the list item and its
                    // btn-inner at this point in time, push the item into a dictionary
                    // that tells us what class to set on it so we can do this after this
                    // processing loop is finished.

                    if (!itemClassDict[itemClass])
                    {
                        itemClassDict[itemClass] = [];
                    }

                    itemClassDict[itemClass].push(item[0]);
                }

                // Set the appropriate listview item classes on each list item
                // and their btn-inner elements. The main reason we didn't do this
                // in the for-loop above is because we can eliminate per-item function overhead
                // by calling addClass() and children() once or twice afterwards. This
                // can give us a significant boost on platforms like WP7.5.

                for (itemClass in itemClassDict)
                {
                    $(itemClassDict[itemClass]).addClass(itemClass).children(".ui-btn-inner").addClass(itemClass);
                }

                $list.find("h1, h2, h3, h4, h5, h6").addClass("ui-li-heading")
			.end()

			.find("p, dl").addClass("ui-li-desc")
			.end()

			.find(".ui-li-aside").each(function()
			{
			    var $this = $(this);
			    $this.prependTo($this.parent()); //shift aside to front for css float
			})
			.end()

			.find(".ui-li-count").each(function()
			{
			    $(this).closest("li").addClass("ui-li-has-count");
			}).addClass("ui-btn-up-" + ($list.jqmData("counttheme") || this.options.countTheme) + " ui-btn-corner-all");

                // The idea here is to look at the first image in the list item
                // itself, and any .ui-link-inherit element it may contain, so we
                // can place the appropriate classes on the image and list item.
                // Note that we used to use something like:
                //
                //    li.find(">img:eq(0), .ui-link-inherit>img:eq(0)").each( ... );
                //
                // But executing a find() like that on Windows Phone 7.5 took a
                // really long time. Walking things manually with the code below
                // allows the 400 listview item page to load in about 3 seconds as
                // opposed to 30 seconds.

                this._addThumbClasses(li);
                this._addThumbClasses($list.find(".ui-link-inherit"));

                this._refreshCorners(create);

                // autodividers binds to this to redraw dividers after the listview refresh
                this._trigger("afterrefresh");
            },

            //create a string for ID/subpage url creation
            _idStringEscape: function(str)
            {
                return str.replace(/[^a-zA-Z0-9]/g, '-');
            },

            _createSubPages: function()
            {
                var parentList = this.element,
			parentPage = parentList.closest(".ui-page"),
			parentUrl = parentPage.jqmData("url"),
			parentId = parentUrl || parentPage[0][$.expando],
			parentListId = parentList.attr("id"),
			o = this.options,
			dns = "data-" + $.mobile.ns,
			self = this,
			persistentFooterID = parentPage.find(":jqmData(role='footer')").jqmData("id"),
			hasSubPages;

                if (typeof listCountPerPage[parentId] === "undefined")
                {
                    listCountPerPage[parentId] = -1;
                }

                parentListId = parentListId || ++listCountPerPage[parentId];

                $(parentList.find("li>ul, li>ol").toArray().reverse()).each(function(i)
                {
                    var self = this,
				list = $(this),
				listId = list.attr("id") || parentListId + "-" + i,
				parent = list.parent(),
				nodeElsFull = $(list.prevAll().toArray().reverse()),
				nodeEls = nodeElsFull.length ? nodeElsFull : $("<span>" + $.trim(parent.contents()[0].nodeValue) + "</span>"),
				title = nodeEls.first().getEncodedText(), //url limits to first 30 chars of text
				id = (parentUrl || "") + "&" + $.mobile.subPageUrlKey + "=" + listId,
				theme = list.jqmData("theme") || o.theme,
				countTheme = list.jqmData("counttheme") || parentList.jqmData("counttheme") || o.countTheme,
				newPage, anchor;

                    //define hasSubPages for use in later removal
                    hasSubPages = true;

                    newPage = list.detach()
						.wrap("<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>")
						.parent()
							.before("<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>")
							.after(persistentFooterID ? $("<div " + dns + "role='footer' " + dns + "id='" + persistentFooterID + "'>") : "")
							.parent()
								.appendTo($.mobile.pageContainer);

                    newPage.page();

                    anchor = parent.find('a:first');

                    if (!anchor.length)
                    {
                        anchor = $("<a/>").html(nodeEls || title).prependTo(parent.empty());
                    }

                    anchor.attr("href", "#" + id);

                }).listview();

                // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
                // and aren't embedded
                if (hasSubPages &&
			parentPage.is(":jqmData(external-page='true')") &&
			parentPage.data("page").options.domCache === false)
                {

                    var newRemove = function(e, ui)
                    {
                        var nextPage = ui.nextPage, npURL,
					prEvent = new $.Event("pageremove");

                        if (ui.nextPage)
                        {
                            npURL = nextPage.jqmData("url");
                            if (npURL.indexOf(parentUrl + "&" + $.mobile.subPageUrlKey) !== 0)
                            {
                                self.childPages().remove();
                                parentPage.trigger(prEvent);
                                if (!prEvent.isDefaultPrevented())
                                {
                                    parentPage.removeWithDependents();
                                }
                            }
                        }
                    };

                    // unbind the original page remove and replace with our specialized version
                    parentPage
				.unbind("pagehide.remove")
				.bind("pagehide.remove", newRemove);
                }
            },

            // TODO sort out a better way to track sub pages of the listview this is brittle
            childPages: function()
            {
                var parentUrl = this.parentPage.jqmData("url");

                return $(":jqmData(url^='" + parentUrl + "&" + $.mobile.subPageUrlKey + "')");
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.listview.prototype.enhanceWithin(e.target);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.mobile.listview.prototype.options.autodividers = false;
        $.mobile.listview.prototype.options.autodividersSelector = function(elt)
        {
            // look for the first anchor in the item
            var text = elt.find('a').text() || elt.text() || null;

            if (!text)
            {
                return null;
            }

            // create the text for the divider (first uppercased letter)
            text = text.slice(0, 1).toUpperCase();

            return text;
        };

        $(document).delegate("ul,ol", "listviewcreate", function()
        {

            var list = $(this),
			listview = list.data("listview");

            if (!listview.options.autodividers)
            {
                return;
            }

            var replaceDividers = function()
            {
                list.find('li:jqmData(role=list-divider)').remove();

                var lis = list.find('li'),
			lastDividerText = null, li, dividerText;

                for (var i = 0; i < lis.length; i++)
                {
                    li = lis[i];
                    dividerText = listview.options.autodividersSelector($(li));

                    if (dividerText && lastDividerText !== dividerText)
                    {
                        var divider = document.createElement('li');
                        divider.appendChild(document.createTextNode(dividerText));
                        divider.setAttribute('data-' + $.mobile.ns + 'role', 'list-divider');
                        li.parentNode.insertBefore(divider, li);
                    }

                    lastDividerText = dividerText;
                }
            };

            var afterListviewRefresh = function()
            {
                list.unbind('listviewafterrefresh', afterListviewRefresh);
                replaceDividers();
                listview.refresh();
                list.bind('listviewafterrefresh', afterListviewRefresh);
            };

            afterListviewRefresh();
        });

    })(jQuery);

    /*
    * "checkboxradio" plugin
    */

    (function($, undefined)
    {

        $.widget("mobile.checkboxradio", $.mobile.widget, {
            options: {
                theme: null,
                initSelector: "input[type='checkbox'],input[type='radio']"
            },
            _create: function()
            {
                var self = this,
			input = this.element,
			inheritAttr = function(input, dataAttr)
			{
			    return input.jqmData(dataAttr) || input.closest("form,fieldset").jqmData(dataAttr);
			},
                // NOTE: Windows Phone could not find the label through a selector
                // filter works though.
			parentLabel = $(input).closest("label"),
			label = parentLabel.length ? parentLabel : $(input).closest("form,fieldset,:jqmData(role='page'),:jqmData(role='dialog')").find("label").filter("[for='" + input[0].id + "']"),
			inputtype = input[0].type,
			mini = inheritAttr(input, "mini"),
			checkedState = inputtype + "-on",
			uncheckedState = inputtype + "-off",
			icon = input.parents(":jqmData(type='horizontal')").length ? undefined : uncheckedState,
			iconpos = inheritAttr(input, "iconpos"),
			activeBtn = icon ? "" : " " + $.mobile.activeBtnClass,
			checkedClass = "ui-" + checkedState + activeBtn,
			uncheckedClass = "ui-" + uncheckedState,
			checkedicon = "ui-icon-" + checkedState,
			uncheckedicon = "ui-icon-" + uncheckedState;

                if (inputtype !== "checkbox" && inputtype !== "radio")
                {
                    return;
                }

                // Expose for other methods
                $.extend(this, {
                    label: label,
                    inputtype: inputtype,
                    checkedClass: checkedClass,
                    uncheckedClass: uncheckedClass,
                    checkedicon: checkedicon,
                    uncheckedicon: uncheckedicon
                });

                // If there's no selected theme check the data attr
                if (!this.options.theme)
                {
                    this.options.theme = $.mobile.getInheritedTheme(this.element, "c");
                }

                label.buttonMarkup({
                    theme: this.options.theme,
                    icon: icon,
                    shadow: false,
                    mini: mini,
                    iconpos: iconpos
                });

                // Wrap the input + label in a div
                var wrapper = document.createElement('div');
                wrapper.className = 'ui-' + inputtype;

                input.add(label).wrapAll(wrapper);

                label.bind({
                    vmouseover: function(event)
                    {
                        if ($(this).parent().is(".ui-disabled"))
                        {
                            event.stopPropagation();
                        }
                    },

                    vclick: function(event)
                    {
                        if (input.is(":disabled"))
                        {
                            event.preventDefault();
                            return;
                        }

                        self._cacheVals();

                        input.prop("checked", inputtype === "radio" && true || !input.prop("checked"));

                        // trigger click handler's bound directly to the input as a substitute for
                        // how label clicks behave normally in the browsers
                        // TODO: it would be nice to let the browser's handle the clicks and pass them
                        //       through to the associate input. we can swallow that click at the parent
                        //       wrapper element level
                        input.triggerHandler('click');

                        // Input set for common radio buttons will contain all the radio
                        // buttons, but will not for checkboxes. clearing the checked status
                        // of other radios ensures the active button state is applied properly
                        self._getInputSet().not(input).prop("checked", false);

                        self._updateAll();
                        return false;
                    }
                });

                input
			.bind({
			    vmousedown: function()
			    {
			        self._cacheVals();
			    },

			    vclick: function()
			    {
			        var $this = $(this);

			        // Adds checked attribute to checked input when keyboard is used
			        if ($this.is(":checked"))
			        {

			            $this.prop("checked", true);
			            self._getInputSet().not($this).prop("checked", false);
			        } else
			        {

			            $this.prop("checked", false);
			        }

			        self._updateAll();
			    },

			    focus: function()
			    {
			        label.addClass($.mobile.focusClass);
			    },

			    blur: function()
			    {
			        label.removeClass($.mobile.focusClass);
			    }
			});

                this.refresh();
            },

            _cacheVals: function()
            {
                this._getInputSet().each(function()
                {
                    $(this).jqmData("cacheVal", this.checked);
                });
            },

            //returns either a set of radios with the same name attribute, or a single checkbox
            _getInputSet: function()
            {
                if (this.inputtype === "checkbox")
                {
                    return this.element;
                }

                return this.element.closest("form,fieldset,:jqmData(role='page'),:jqmData(role='dialog')")
			.find("input[name='" + this.element[0].name + "'][type='" + this.inputtype + "']");
            },

            _updateAll: function()
            {
                var self = this;

                this._getInputSet().each(function()
                {
                    var $this = $(this);

                    if (this.checked || self.inputtype === "checkbox")
                    {
                        $this.trigger("change");
                    }
                })
		.checkboxradio("refresh");
            },

            refresh: function()
            {
                var input = this.element[0],
			label = this.label,
			icon = label.find(".ui-icon");

                if (input.checked)
                {
                    label.addClass(this.checkedClass).removeClass(this.uncheckedClass);
                    icon.addClass(this.checkedicon).removeClass(this.uncheckedicon);
                } else
                {
                    label.removeClass(this.checkedClass).addClass(this.uncheckedClass);
                    icon.removeClass(this.checkedicon).addClass(this.uncheckedicon);
                }

                if (input.disabled)
                {
                    this.disable();
                } else
                {
                    this.enable();
                }
            },

            disable: function()
            {
                this.element.prop("disabled", true).parent().addClass("ui-disabled");
            },

            enable: function()
            {
                this.element.prop("disabled", false).parent().removeClass("ui-disabled");
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.checkboxradio.prototype.enhanceWithin(e.target, true);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.widget("mobile.button", $.mobile.widget, {
            options: {
                theme: null,
                icon: null,
                iconpos: null,
                corners: true,
                shadow: true,
                iconshadow: true,
                initSelector: "button, [type='button'], [type='submit'], [type='reset']"
            },
            _create: function()
            {
                var $el = this.element,
			$button,
			o = this.options,
			type,
			name,
			inline = o.inline || $el.jqmData("inline"),
			mini = o.mini || $el.jqmData("mini"),
			classes = "",
			$buttonPlaceholder;

                // if this is a link, check if it's been enhanced and, if not, use the right function
                if ($el[0].tagName === "A")
                {
                    if (!$el.hasClass("ui-btn"))
                    {
                        $el.buttonMarkup();
                    }

                    return;
                }

                // get the inherited theme
                // TODO centralize for all widgets
                if (!this.options.theme)
                {
                    this.options.theme = $.mobile.getInheritedTheme(this.element, "c");
                }

                // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577
                /* if( $el[0].className.length ) {
                classes = $el[0].className;
                } */
                if (!! ~$el[0].className.indexOf("ui-btn-left"))
                {
                    classes = "ui-btn-left";
                }

                if (!! ~$el[0].className.indexOf("ui-btn-right"))
                {
                    classes = "ui-btn-right";
                }

                if ($el.attr("type") === "submit" || $el.attr("type") === "reset")
                {
                    classes += "ui-submit";
                }
                $("label[for='" + $el.attr("id") + "']").addClass("ui-submit");

                // Add ARIA role
                this.button = $("<div></div>")
			[$el.html() ? "html" : "text"]($el.html() || $el.val())
			.insertBefore($el)
			.buttonMarkup({
			    theme: o.theme,
			    icon: o.icon,
			    iconpos: o.iconpos,
			    inline: inline,
			    corners: o.corners,
			    shadow: o.shadow,
			    iconshadow: o.iconshadow,
			    mini: mini
			})
			.addClass(classes)
			.append($el.addClass("ui-btn-hidden"));

                $button = this.button;
                type = $el.attr("type");
                name = $el.attr("name");

                // Add hidden input during submit if input type="submit" has a name.
                if (type !== "button" && type !== "reset" && name)
                {
                    $el.bind("vclick", function()
                    {
                        // Add hidden input if it doesn't already exist.
                        if ($buttonPlaceholder === undefined)
                        {
                            $buttonPlaceholder = $("<input>", {
                                type: "hidden",
                                name: $el.attr("name"),
                                value: $el.attr("value")
                            }).insertBefore($el);

                            // Bind to doc to remove after submit handling
                            $(document).one("submit", function()
                            {
                                $buttonPlaceholder.remove();

                                // reset the local var so that the hidden input
                                // will be re-added on subsequent clicks
                                $buttonPlaceholder = undefined;
                            });
                        }
                    });
                }

                $el.bind({
                    focus: function()
                    {
                        $button.addClass($.mobile.focusClass);
                    },

                    blur: function()
                    {
                        $button.removeClass($.mobile.focusClass);
                    }
                });

                this.refresh();
            },

            enable: function()
            {
                this.element.attr("disabled", false);
                this.button.removeClass("ui-disabled").attr("aria-disabled", false);
                return this._setOption("disabled", false);
            },

            disable: function()
            {
                this.element.attr("disabled", true);
                this.button.addClass("ui-disabled").attr("aria-disabled", true);
                return this._setOption("disabled", true);
            },

            refresh: function()
            {
                var $el = this.element;

                if ($el.prop("disabled"))
                {
                    this.disable();
                } else
                {
                    this.enable();
                }

                // Grab the button's text element from its implementation-independent data item
                $(this.button.data('buttonElements').text)[$el.html() ? "html" : "text"]($el.html() || $el.val());
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.button.prototype.enhanceWithin(e.target, true);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.fn.controlgroup = function(options)
        {
            function flipClasses(els, flCorners)
            {
                els.removeClass("ui-btn-corner-all ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-controlgroup-last ui-shadow")
			.eq(0).addClass(flCorners[0])
			.end()
			.last().addClass(flCorners[1]).addClass("ui-controlgroup-last");
            }

            return this.each(function()
            {
                var $el = $(this),
			o = $.extend({
			    direction: $el.jqmData("type") || "vertical",
			    shadow: false,
			    excludeInvisible: true,
			    mini: $el.jqmData("mini")
			}, options),
			groupheading = $el.children("legend"),
			flCorners = o.direction === "horizontal" ? ["ui-corner-left", "ui-corner-right"] : ["ui-corner-top", "ui-corner-bottom"],
			type = $el.find("input").first().attr("type");

                $el.wrapInner("<div class='ui-controlgroup-controls'></div>");

                // Replace legend with more stylable replacement div
                if (groupheading.length)
                {
                    $("<div role='heading' class='ui-controlgroup-label'>" + groupheading.html() + "</div>").insertBefore($el.children(0));
                    groupheading.remove();
                }

                $el.addClass("ui-corner-all ui-controlgroup ui-controlgroup-" + o.direction);

                flipClasses($el.find(".ui-btn" + (o.excludeInvisible ? ":visible" : "")).not('.ui-slider-handle'), flCorners);
                flipClasses($el.find(".ui-btn-inner"), flCorners);

                if (o.shadow)
                {
                    $el.addClass("ui-shadow");
                }

                if (o.mini)
                {
                    $el.addClass("ui-mini");
                }

            });
        };

        // The pagecreate handler for controlgroup is in jquery.mobile.init because of the soft-dependency on the wrapped widgets

    })(jQuery);

    (function($, undefined)
    {

        $(document).bind("pagecreate create", function(e)
        {

            //links within content areas, tests included with page
            $(e.target)
		.find("a")
		.jqmEnhanceable()
		.not(".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')")
		.addClass("ui-link");

        });

    })(jQuery);


    (function($, undefined)
    {

        function fitSegmentInsideSegment(winSize, segSize, offset, desired)
        {
            var ret = desired;

            if (winSize < segSize)
            {
                // Center segment if it's bigger than the window
                ret = offset + (winSize - segSize) / 2;
            } else
            {
                // Otherwise center it at the desired coordinate while keeping it completely inside the window
                ret = Math.min(Math.max(offset, desired - segSize / 2), offset + winSize - segSize);
            }

            return ret;
        }

        $.widget("mobile.popup", $.mobile.widget, {
            options: {
                theme: null,
                overlayTheme: null,
                shadow: true,
                corners: true,
                transition: $.mobile.defaultDialogTransition,
                initSelector: ":jqmData(role='popup')"
            },

            _create: function()
            {
                var ui = {
                    screen: $("<div class='ui-screen-hidden ui-popup-screen fade'></div>"),
                    placeholder: $("<div style='display: none;'><!-- placeholder --></div>"),
                    container: $("<div class='ui-popup-container ui-selectmenu-hidden'></div>")
                },
				eatEventAndClose = function(e)
				{
				    e.preventDefault();
				    e.stopImmediatePropagation();
				    self.close();
				},
				thisPage = this.element.closest(".ui-page"),
				myId = this.element.attr("id"),
				self = this;

                if (thisPage.length === 0)
                {
                    thisPage = $("body");
                }

                // Apply the proto
                thisPage.append(ui.screen);
                ui.container.insertAfter(ui.screen);
                // Leave a placeholder where the element used to be
                ui.placeholder.insertAfter(this.element);
                if (myId)
                {
                    ui.placeholder.html("<!-- placeholder for " + myId + " -->");
                }
                ui.container.append(this.element);

                // Add class to popup element 
                this.element.addClass("ui-popup");

                // Define instance variables
                $.extend(this, {
                    _page: thisPage,
                    _ui: ui,
                    _fallbackTransition: "",
                    _currentTransition: false,
                    _prereqs: null,
                    _isOpen: false,
                    _globalHandlers: [
					{
					    src: $(window),
					    handler: {
					        resize: function(e)
					        {
					            if (self._isOpen)
					            {
					                self._resizeScreen();
					            }
					        },
					        keyup: function(e)
					        {
					            if (self._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE)
					            {
					                eatEventAndClose(e);
					            }
					        }
					    }
					}
				]
                });

                $.each(this.options, function(key, value)
                {
                    // Cause initial options to be applied by their handler by temporarily setting the option to undefined
                    // - the handler then sets it to the initial value
                    self.options[key] = undefined;
                    self._setOption(key, value, true);
                });

                ui.screen.bind("vclick", function(e) { eatEventAndClose(e); });

                $.each(this._globalHandlers, function(idx, value)
                {
                    value.src.bind(value.handler);
                });
            },

            _resizeScreen: function()
            {
                this._ui.screen.height(Math.max($(window).height(), this._page.height()));
            },

            _applyTheme: function(dst, theme)
            {
                var classes = (dst.attr("class") || "").split(" "),
				alreadyAdded = true,
				currentTheme = null,
				matches,
				themeStr = String(theme);

                while (classes.length > 0)
                {
                    currentTheme = classes.pop();
                    matches = currentTheme.match(/^ui-body-([a-z])$/);
                    if (matches && matches.length > 1)
                    {
                        currentTheme = matches[1];
                        break;
                    } else
                    {
                        currentTheme = null;
                    }
                }

                if (theme !== currentTheme)
                {
                    dst.removeClass("ui-body-" + currentTheme);
                    if (!(theme === null || theme === "none"))
                    {
                        dst.addClass("ui-body-" + themeStr);
                    }
                }
            },

            _setTheme: function(value)
            {
                this._applyTheme(this._ui.container, value);
            },

            _setOverlayTheme: function(value)
            {
                this._applyTheme(this._ui.screen, value);

                if ($.mobile.browser.ie)
                {
                    this._ui.screen.toggleClass(
					"ui-popup-screen-background-hack",
					(this._ui.screen.css("background-color") === "transparent" &&
						this._ui.screen.css("background-image") === "none" &&
						this._ui.screen.css("background") === undefined));
                }
            },

            _setShadow: function(value)
            {
                this._ui.container.toggleClass("ui-overlay-shadow", value);
            },

            _setCorners: function(value)
            {
                this._ui.container.toggleClass("ui-corner-all", value);
            },

            _applyTransition: function(value)
            {
                this._ui.container.removeClass(this._fallbackTransition);
                if (value && value !== "none")
                {
                    this._fallbackTransition = $.mobile._maybeDegradeTransition(value);
                    this._ui.container.addClass(this._fallbackTransition);
                }
            },

            _setTransition: function(value)
            {
                if (!this._currentTransition)
                {
                    this._applyTransition(value);
                }
            },

            _setOption: function(key, value)
            {
                var setter = "_set" + key.replace(/^[a-z]/, function(c) { return c.toUpperCase(); });

                if (this[setter] !== undefined)
                {
                    this[setter](value);
                    // Record the option change in the options and in the DOM data-* attributes
                    this.options[key] = value;
                    this.element.attr("data-" + ($.mobile.ns || "") + (key.replace(/([A-Z])/, "-$1").toLowerCase()), value);
                } else
                {
                    $.mobile.widget.prototype._setOption.apply(this, arguments);
                }
            },

            // Try and center the overlay over the given coordinates
            _placementCoords: function(x, y)
            {
                // Tolerances off the window edges
                var tol = { l: 15, t: 30, r: 15, b: 30 },
                // rectangle within which the popup must fit
				rc = {
				    l: tol.l,
				    t: $(window).scrollTop() + tol.t,
				    cx: $(window).width() - tol.l - tol.r,
				    cy: $(window).height() - tol.t - tol.b
				},
				menuSize;

                // Clamp the width of the menu before grabbing its size
                this._ui.container.css("max-width", rc.cx);
                menuSize = {
                    cx: this._ui.container.outerWidth(true),
                    cy: this._ui.container.outerHeight(true)
                };

                return {
                    x: fitSegmentInsideSegment(rc.cx, menuSize.cx, rc.l, x),
                    y: fitSegmentInsideSegment(rc.cy, menuSize.cy, rc.t, y)
                };
            },

            _immediate: function()
            {
                if (this._prereqs)
                {
                    $.each(this._prereqs, function(key, val)
                    {
                        val.resolve();
                    });
                }
            },

            _createPrereqs: function(screenPrereq, containerPrereq, whenDone)
            {
                var self = this, prereqs;

                prereqs = {
                    screen: $.Deferred(function(d)
                    {
                        d.then(function()
                        {
                            if (prereqs === self._prereqs)
                            {
                                screenPrereq();
                            }
                        });
                    }),
                    container: $.Deferred(function(d)
                    {
                        d.then(function()
                        {
                            if (prereqs === self._prereqs)
                            {
                                containerPrereq();
                            }
                        });
                    })
                };

                $.when(prereqs.screen, prereqs.container).done(function()
                {
                    if (prereqs === self._prereqs)
                    {
                        self._prereqs = null;
                        whenDone();
                    }
                });

                self._prereqs = prereqs;
            },

            _animate: function(args)
            {
                var self = this;

                if (self.options.overlayTheme && args.additionalCondition)
                {
                    self._ui.screen
					.removeClass(args.classToRemove)
					.addClass(args.screenClassToAdd)
					.animationComplete(function()
					{
					    args.prereqs.screen.resolve();
					});
                } else
                {
                    args.prereqs.screen.resolve();
                }

                if (args.transition && args.transition !== "none")
                {
                    if (args.applyTransition) { self._applyTransition(args.transition); }
                    self._ui.container
					.addClass(args.containerClassToAdd)
					.removeClass(args.classToRemove)
					.animationComplete(function()
					{
					    args.prereqs.container.resolve();
					});
                } else
                {
                    args.prereqs.container.resolve();
                }
            },

            _open: function(x, y, transition)
            {
                var self = this,
				coords = self._placementCoords(
					(undefined === x ? window.innerWidth / 2 : x),
					(undefined === y ? window.innerHeight / 2 : y));

                // Count down to triggering "opened" - we have two prerequisites:
                // 1. The popup window animation completes (container())
                // 2. The screen opacity animation completes (screen())
                self._createPrereqs(
				$.noop,
				function()
				{
				    self._applyTransition("none");
				    self._ui.container.removeClass("in");
				    self._resizeScreen();
				},
				function()
				{
				    self._isOpen = true;
				    self._ui.container.attr("tabindex", "0").focus();
				    self.element.trigger("opened");
				});

                if (transition)
                {
                    self._currentTransition = transition;
                    self._applyTransition(transition);
                } else
                {
                    transition = self.options.transition;
                }

                if (!self.options.theme)
                {
                    self._setTheme(self._page.jqmData("theme") || $.mobile.getInheritedTheme(self._page, "c"));
                }

                self._resizeScreen();
                self._ui.screen.removeClass("ui-screen-hidden");

                self._ui.container
				.removeClass("ui-selectmenu-hidden")
				.offset({
				    left: coords.x,
				    top: coords.y
				});

                self._animate({
                    additionalCondition: true,
                    transition: transition,
                    classToRemove: "",
                    screenClassToAdd: "in",
                    containerClassToAdd: "in",
                    applyTransition: false,
                    prereqs: self._prereqs
                });
            },

            _close: function()
            {
                var self = this,
				transition = (self._currentTransition ? self._currentTransition : self.options.transition);

                this._isOpen = false;

                // Count down to triggering "closed" - we have two prerequisites:
                // 1. The popup window reverse animation completes (container())
                // 2. The screen opacity animation completes (screen())
                self._createPrereqs(
				function()
				{
				    self._ui.screen
						.removeClass("out")
						.addClass("ui-screen-hidden");
				},
				function()
				{
				    self._ui.container
						.removeClass("reverse out")
						.addClass("ui-selectmenu-hidden")
						.removeAttr("style");
				},
				function()
				{
				    self._ui.container.removeAttr("tabindex");
				    self.element.trigger("closed");
				});

                self._animate({
                    additionalCondition: self._ui.screen.hasClass("in"),
                    transition: transition,
                    classToRemove: "in",
                    screenClassToAdd: "out",
                    containerClassToAdd: "reverse out",
                    applyTransition: true,
                    prereqs: self._prereqs
                });
            },

            _destroy: function()
            {
                // Put the element back to where the placeholder was and remove the "ui-popup" class
                this.element
				.insertAfter(this._ui.placeholder)
				.removeClass("ui-popup");
                this._ui.screen.remove();
                this._ui.container.remove();
                this._ui.placeholder.remove();

                $.each(this._globalHandlers, function(idx, oneSrc)
                {
                    $.each(oneSrc.handler, function(eventType, handler)
                    {
                        oneSrc.src.unbind(eventType, handler);
                    });
                });
            },

            open: function(x, y, transition)
            {
                $.mobile.popup.popupManager.push(this, arguments);
            },

            close: function()
            {
                $.mobile.popup.popupManager.pop(this);
            }
        });

        // Popup manager, whose policy is to ignore requests for opening popups when a popup is already in
        // the process of opening, or already open
        $.mobile.popup.popupManager = {
            _currentlyOpenPopup: null,
            _popupIsOpening: false,
            _popupIsClosing: false,
            _abort: false,

            // Call _onHashChange if the hash changes /after/ the popup is on the screen
            // Note that placing the popup on the screen can itself cause a hashchange,
            // because the dialogHashKey may need to be added to the URL.
            _navHook: function(whenHooked)
            {
                var self = this, dstHash;
                function realInstallListener()
                {
                    $(window).one("hashchange.popup", function()
                    {
                        self._onHashChange();
                    });
                    whenHooked();
                }

                self._myUrl = $.mobile.activePage.jqmData("url");
                $.mobile.pageContainer.one("pagebeforechange.popup", function(e, data)
                {
                    var parsedDst, toUrl;

                    if (typeof data.toPage === "string")
                    {
                        parsedDst = data.toPage;
                    } else
                    {
                        parsedDst = data.toPage.jqmData("url");
                    }
                    parsedDst = $.mobile.path.parseUrl(parsedDst);
                    toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;

                    if (self._myUrl !== toUrl)
                    {
                        self._onHashChange(true);
                    }
                });
                if ($.mobile.hashListeningEnabled)
                {
                    var activeEntry = $.mobile.urlHistory.getActive(),
					dstTransition,
					currentIsDialog = $.mobile.activePage.is(".ui-dialog"),
					hasHash = (activeEntry.url.indexOf($.mobile.dialogHashKey) > -1) && !currentIsDialog;

                    if ($.mobile.urlHistory.activeIndex === 0)
                    {
                        dstTransition = $.mobile.defaultDialogTransition;
                    } else
                    {
                        dstTransition = activeEntry.transition;
                    }

                    if (hasHash)
                    {
                        realInstallListener();
                    } else
                    {
                        $(window).one("hashchange.popupBinder", function()
                        {
                            realInstallListener();
                        });
                        dstHash = activeEntry.url + $.mobile.dialogHashKey;
                        if ($.mobile.urlHistory.activeIndex === 0 && dstHash === $.mobile.urlHistory.initialDst)
                        {
                            dstHash += $.mobile.dialogHashKey;
                        }
                        $.mobile.urlHistory.ignoreNextHashChange = currentIsDialog;
                        $.mobile.path.set(dstHash);
                        $.mobile.urlHistory.addNew(dstHash, dstTransition, activeEntry.title, activeEntry.pageUrl, activeEntry.role);
                    }
                } else
                {
                    whenHooked();
                }
            },

            _navUnhook: function(abort)
            {
                if (abort)
                {
                    $(window).unbind("hashchange.popupBinder hashchange.popup");
                }

                if ($.mobile.hashListeningEnabled && !abort)
                {
                    window.history.back();
                }
                else
                {
                    this._onHashChange();
                }
                $.mobile.activePage.unbind("pagebeforechange.popup");
            },

            push: function(popup, args)
            {
                var self = this;

                if (!self._currentlyOpenPopup)
                {
                    self._currentlyOpenPopup = popup;

                    self._navHook(function()
                    {
                        self._popupIsOpening = true;
                        self._currentlyOpenPopup.element.one("opened", function()
                        {
                            self._popupIsOpening = false;
                        });
                        self._currentlyOpenPopup._open.apply(self._currentlyOpenPopup, args);
                        if (!self._popupIsOpening && self._abort)
                        {
                            self._currentlyOpenPopup._immediate();
                        }
                    });
                }
            },

            pop: function(popup)
            {
                var self = this;

                if (popup === self._currentlyOpenPopup && !self._popupIsClosing)
                {
                    self._popupIsClosing = true;
                    if (self._popupIsOpening)
                    {
                        self._currentlyOpenPopup.element.one("opened", $.proxy(self, "_navUnhook"));
                    } else
                    {
                        self._navUnhook();
                    }
                }
            },

            _onHashChange: function(immediate)
            {
                var self = this;

                self._abort = immediate;

                if (self._currentlyOpenPopup)
                {
                    if (immediate && self._popupIsOpening)
                    {
                        self._currentlyOpenPopup._immediate();
                    }
                    self._popupIsClosing = true;
                    self._currentlyOpenPopup.element.one("closed", function()
                    {
                        self._popupIsClosing = false;
                        self._currentlyOpenPopup = null;
                        $(self).trigger("done");
                    });
                    self._currentlyOpenPopup._close();
                    if (immediate && self._currentlyOpenPopup)
                    {
                        self._currentlyOpenPopup._immediate();
                    }
                }
            }
        };

        $.mobile.popup.handleLink = function($link)
        {
            var closestPage = $link.closest(":jqmData(role='page')"),
			scope = ((closestPage.length === 0) ? $("body") : closestPage),
			popup = $($link.attr("href"), scope[0]),
			offset;

            if (popup.data("popup"))
            {
                offset = $link.offset();

                popup
				.popup("open",
					offset.left + $link.outerWidth() / 2,
					offset.top + $link.outerHeight() / 2,
					$link.jqmData("transition"));

                // If this link is not inside a popup, re-focus onto it after the popup(s) complete
                // For some reason, a $.proxy( $link, "focus" ) doesn't work as the handler
                if ($link.parents(".ui-popup-container").length === 0)
                {
                    $($.mobile.popup.popupManager).one("done", function()
                    {
                        $link.focus();
                    });
                }
            }

            //remove after delay
            setTimeout(function()
            {
                $link.removeClass($.mobile.activeBtnClass);
            }, 300);
        };

        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.popup.prototype.enhanceWithin(e.target, true);
        });

    })(jQuery);

    (function($)
    {
        var meta = $("meta[name=viewport]"),
		initialContent = meta.attr("content"),
		disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no",
		enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes",
		disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test(initialContent);

        $.mobile.zoom = $.extend({}, {
            enabled: !disabledInitially,
            locked: false,
            disable: function(lock)
            {
                if (!disabledInitially && !$.mobile.zoom.locked)
                {
                    meta.attr("content", disabledZoom);
                    $.mobile.zoom.enabled = false;
                    $.mobile.zoom.locked = lock || false;
                }
            },
            enable: function(unlock)
            {
                if (!disabledInitially && (!$.mobile.zoom.locked || unlock === true))
                {
                    meta.attr("content", enabledZoom);
                    $.mobile.zoom.enabled = true;
                    $.mobile.zoom.locked = false;
                }
            },
            restore: function()
            {
                if (!disabledInitially)
                {
                    meta.attr("content", initialContent);
                    $.mobile.zoom.enabled = true;
                }
            }
        });

    } (jQuery));

    (function($, undefined)
    {

        $.widget("mobile.textinput", $.mobile.widget, {
            options: {
                theme: null,
                // This option defaults to true on iOS devices.
                preventFocusZoom: /iPhone|iPad|iPod/.test(navigator.platform) && navigator.userAgent.indexOf("AppleWebKit") > -1,
                initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])",
                clearSearchButtonText: "clear text"
            },

            _create: function()
            {

                var input = this.element,
			o = this.options,
			theme = o.theme || $.mobile.getInheritedTheme(this.element, "c"),
			themeclass = " ui-body-" + theme,
			mini = input.jqmData("mini") === true,
			miniclass = mini ? " ui-mini" : "",
			focusedEl, clearbtn;

                function toggleClear()
                {
                    setTimeout(function()
                    {
                        clearbtn.toggleClass("ui-input-clear-hidden", !input.val());
                    }, 0);
                }

                $("label[for='" + input.attr("id") + "']").addClass("ui-input-text");

                focusedEl = input.addClass("ui-input-text ui-body-" + theme);

                // XXX: Temporary workaround for issue 785 (Apple bug 8910589).
                //      Turn off autocorrect and autocomplete on non-iOS 5 devices
                //      since the popup they use can't be dismissed by the user. Note
                //      that we test for the presence of the feature by looking for
                //      the autocorrect property on the input element. We currently
                //      have no test for iOS 5 or newer so we're temporarily using
                //      the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - jblas
                if (typeof input[0].autocorrect !== "undefined" && !$.support.touchOverflow)
                {
                    // Set the attribute instead of the property just in case there
                    // is code that attempts to make modifications via HTML.
                    input[0].setAttribute("autocorrect", "off");
                    input[0].setAttribute("autocomplete", "off");
                }


                //"search" input widget
                if (input.is("[type='search'],:jqmData(type='search')"))
                {

                    focusedEl = input.wrap("<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield" + themeclass + miniclass + "'></div>").parent();
                    clearbtn = $("<a href='#' class='ui-input-clear' title='" + o.clearSearchButtonText + "'>" + o.clearSearchButtonText + "</a>")
				.bind('click', function(event)
				{
				    input
						.val("")
						.focus()
						.trigger("change");
				    clearbtn.addClass("ui-input-clear-hidden");
				    event.preventDefault();
				})
				.appendTo(focusedEl)
				.buttonMarkup({
				    icon: "delete",
				    iconpos: "notext",
				    corners: true,
				    shadow: true,
				    mini: mini
				});

                    toggleClear();

                    input.bind('paste cut keyup focus change blur', toggleClear);

                } else
                {
                    input.addClass("ui-corner-all ui-shadow-inset" + themeclass + miniclass);
                }

                input.focus(function()
                {
                    focusedEl.addClass($.mobile.focusClass);
                })
			.blur(function()
			{
			    focusedEl.removeClass($.mobile.focusClass);
			})
                // In many situations, iOS will zoom into the select upon tap, this prevents that from happening
			.bind("focus", function()
			{
			    if (o.preventFocusZoom)
			    {
			        $.mobile.zoom.disable(true);
			    }
			})
			.bind("blur", function()
			{
			    if (o.preventFocusZoom)
			    {
			        $.mobile.zoom.enable(true);
			    }
			});

                // Autogrow
                if (input.is("textarea"))
                {
                    var extraLineHeight = 15,
				keyupTimeoutBuffer = 100,
				keyup = function()
				{
				    var scrollHeight = input[0].scrollHeight,
						clientHeight = input[0].clientHeight;

				    if (clientHeight < scrollHeight)
				    {
				        input.height(scrollHeight + extraLineHeight);
				    }
				},
				keyupTimeout;

                    input.keyup(function()
                    {
                        clearTimeout(keyupTimeout);
                        keyupTimeout = setTimeout(keyup, keyupTimeoutBuffer);
                    });

                    // binding to pagechange here ensures that for pages loaded via
                    // ajax the height is recalculated without user input
                    $(document).one("pagechange", keyup);

                    // Issue 509: the browser is not providing scrollHeight properly until the styles load
                    if ($.trim(input.val()))
                    {
                        // bind to the window load to make sure the height is calculated based on BOTH
                        // the DOM and CSS
                        $(window).load(keyup);
                    }
                }
            },

            disable: function()
            {
                (this.element.attr("disabled", true).is("[type='search'],:jqmData(type='search')") ?
			this.element.parent() : this.element).addClass("ui-disabled");
            },

            enable: function()
            {
                (this.element.attr("disabled", false).is("[type='search'],:jqmData(type='search')") ?
			this.element.parent() : this.element).removeClass("ui-disabled");
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.textinput.prototype.enhanceWithin(e.target, true);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.mobile.listview.prototype.options.filter = false;
        $.mobile.listview.prototype.options.filterPlaceholder = "Filter items...";
        $.mobile.listview.prototype.options.filterTheme = "c";
        // TODO rename callback/deprecate and default to the item itself as the first argument
        $.mobile.listview.prototype.options.filterCallback = function(text, searchValue, item)
        {
            return text.toString().toLowerCase().indexOf(searchValue) === -1;
        };

        $(document).delegate(":jqmData(role='listview')", "listviewcreate", function()
        {

            var list = $(this),
		listview = list.data("listview");

            if (!listview.options.filter)
            {
                return;
            }

            var wrapper = $("<form>", {
                "class": "ui-listview-filter ui-bar-" + listview.options.filterTheme,
                "role": "search"
            }),
		search = $("<input>", {
		    placeholder: listview.options.filterPlaceholder
		})
		.attr("data-" + $.mobile.ns + "type", "search")
		.jqmData("lastval", "")
		.bind("keyup change", function()
		{

		    var $this = $(this),
				val = this.value.toLowerCase(),
				listItems = null,
				lastval = $this.jqmData("lastval") + "",
				childItems = false,
				itemtext = "",
				item;

		    // Change val as lastval for next execution
		    $this.jqmData("lastval", val);
		    if (val.length < lastval.length || val.indexOf(lastval) !== 0)
		    {

		        // Removed chars or pasted something totally different, check all items
		        listItems = list.children();
		    } else
		    {

		        // Only chars added, not removed, only use visible subset
		        listItems = list.children(":not(.ui-screen-hidden)");
		    }

		    if (val)
		    {

		        // This handles hiding regular rows without the text we search for
		        // and any list dividers without regular rows shown under it

		        for (var i = listItems.length - 1; i >= 0; i--)
		        {
		            item = $(listItems[i]);
		            itemtext = item.jqmData("filtertext") || item.text();

		            if (item.is("li:jqmData(role=list-divider)"))
		            {

		                item.toggleClass("ui-filter-hidequeue", !childItems);

		                // New bucket!
		                childItems = false;

		            } else if (listview.options.filterCallback(itemtext, val, item))
		            {

		                //mark to be hidden
		                item.toggleClass("ui-filter-hidequeue", true);
		            } else
		            {

		                // There's a shown item in the bucket
		                childItems = true;
		            }
		        }

		        // Show items, not marked to be hidden
		        listItems
					.filter(":not(.ui-filter-hidequeue)")
					.toggleClass("ui-screen-hidden", false);

		        // Hide items, marked to be hidden
		        listItems
					.filter(".ui-filter-hidequeue")
					.toggleClass("ui-screen-hidden", true)
					.toggleClass("ui-filter-hidequeue", false);

		    } else
		    {

		        //filtervalue is empty => show all
		        listItems.toggleClass("ui-screen-hidden", false);
		    }
		    listview._refreshCorners();
		})
		.appendTo(wrapper)
		.textinput();

            if (listview.options.inset)
            {
                wrapper.addClass("ui-listview-filter-inset");
            }

            wrapper.bind("submit", function()
            {
                return false;
            })
	.insertBefore(list);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.widget("mobile.slider", $.mobile.widget, {
            options: {
                theme: null,
                trackTheme: null,
                disabled: false,
                initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",
                mini: false
            },

            _create: function()
            {

                // TODO: Each of these should have comments explain what they're for
                var self = this,

			control = this.element,

			parentTheme = $.mobile.getInheritedTheme(control, "c"),

			theme = this.options.theme || parentTheme,

			trackTheme = this.options.trackTheme || parentTheme,

			cType = control[0].nodeName.toLowerCase(),

			selectClass = (cType === "select") ? "ui-slider-switch" : "",

			controlID = control.attr("id"),

			$label = $("[for='" + controlID + "']"),

			labelID = $label.attr("id") || controlID + "-label",

			label = $label.attr("id", labelID),

			val = function()
			{
			    return cType === "input" ? parseFloat(control.val()) : control[0].selectedIndex;
			},

			min = cType === "input" ? parseFloat(control.attr("min")) : 0,

			max = cType === "input" ? parseFloat(control.attr("max")) : control.find("option").length - 1,

			step = window.parseFloat(control.attr("step") || 1),

			inlineClass = (this.options.inline || control.jqmData("inline") === true) ? " ui-slider-inline" : "",

			miniClass = (this.options.mini || control.jqmData("mini")) ? " ui-slider-mini" : "",


			domHandle = document.createElement('a'),
			handle = $(domHandle),
			domSlider = document.createElement('div'),
			slider = $(domSlider),

			valuebg = control.jqmData("highlight") && cType !== "select" ? (function()
			{
			    var bg = document.createElement('div');
			    bg.className = 'ui-slider-bg ' + $.mobile.activeBtnClass + ' ui-btn-corner-all';
			    return $(bg).prependTo(slider);
			})() : false,

			options;

                domHandle.setAttribute('href', "#");
                domSlider.setAttribute('role', 'application');
                domSlider.className = ['ui-slider ', selectClass, " ui-btn-down-", trackTheme, ' ui-btn-corner-all', inlineClass, miniClass].join("");
                domHandle.className = 'ui-slider-handle';
                domSlider.appendChild(domHandle);

                handle.buttonMarkup({ corners: true, theme: theme, shadow: true })
				.attr({
				    "role": "slider",
				    "aria-valuemin": min,
				    "aria-valuemax": max,
				    "aria-valuenow": val(),
				    "aria-valuetext": val(),
				    "title": val(),
				    "aria-labelledby": labelID
				});

                $.extend(this, {
                    slider: slider,
                    handle: handle,
                    valuebg: valuebg,
                    dragging: false,
                    beforeStart: null,
                    userModified: false,
                    mouseMoved: false
                });

                if (cType === "select")
                {
                    var wrapper = document.createElement('div');
                    wrapper.className = 'ui-slider-inneroffset';

                    for (var j = 0, length = domSlider.childNodes.length; j < length; j++)
                    {
                        wrapper.appendChild(domSlider.childNodes[j]);
                    }

                    domSlider.appendChild(wrapper);

                    // slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );

                    // make the handle move with a smooth transition
                    handle.addClass("ui-slider-handle-snapping");

                    options = control.find("option");

                    for (var i = 0, optionsCount = options.length; i < optionsCount; i++)
                    {
                        var side = !i ? "b" : "a",
					sliderTheme = !i ? " ui-btn-down-" + trackTheme : (" " + $.mobile.activeBtnClass),
					sliderLabel = document.createElement('div'),
					sliderImg = document.createElement('span');

                        sliderImg.className = ['ui-slider-label ui-slider-label-', side, sliderTheme, " ui-btn-corner-all"].join("");
                        sliderImg.setAttribute('role', 'img');
                        sliderImg.appendChild(document.createTextNode(options[i].innerHTML));
                        $(sliderImg).prependTo(slider);
                    }

                    self._labels = $(".ui-slider-label", slider);

                }

                label.addClass("ui-slider");

                // monitor the input for updated values
                control.addClass(cType === "input" ? "ui-slider-input" : "ui-slider-switch")
			.change(function()
			{
			    // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
			    if (!self.mouseMoved)
			    {
			        self.refresh(val(), true);
			    }
			})
			.keyup(function()
			{ // necessary?
			    self.refresh(val(), true, true);
			})
			.blur(function()
			{
			    self.refresh(val(), true);
			});

                // prevent screen drag when slider activated
                $(document).bind("vmousemove", function(event)
                {
                    if (self.dragging)
                    {
                        // self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
                        self.mouseMoved = true;

                        if (cType === "select")
                        {
                            // make the handle move in sync with the mouse
                            handle.removeClass("ui-slider-handle-snapping");
                        }

                        self.refresh(event);

                        // only after refresh() you can calculate self.userModified
                        self.userModified = self.beforeStart !== control[0].selectedIndex;
                        return false;
                    }
                });

                slider.bind("vmousedown", function(event)
                {
                    self.dragging = true;
                    self.userModified = false;
                    self.mouseMoved = false;

                    if (cType === "select")
                    {
                        self.beforeStart = control[0].selectedIndex;
                    }

                    self.refresh(event);
                    return false;
                })
		.bind("vclick", false);

                slider.add(document)
			.bind("vmouseup", function()
			{
			    if (self.dragging)
			    {

			        self.dragging = false;

			        if (cType === "select")
			        {

			            // make the handle move with a smooth transition
			            handle.addClass("ui-slider-handle-snapping");

			            if (self.mouseMoved)
			            {

			                // this is a drag, change the value only if user dragged enough
			                if (self.userModified)
			                {
			                    self.refresh(self.beforeStart === 0 ? 1 : 0);
			                }
			                else
			                {
			                    self.refresh(self.beforeStart);
			                }

			            }
			            else
			            {
			                // this is just a click, change the value
			                self.refresh(self.beforeStart === 0 ? 1 : 0);
			            }

			        }

			        self.mouseMoved = false;

			        return false;
			    }
			});

                slider.insertAfter(control);

                // Only add focus class to toggle switch, sliders get it automatically from ui-btn
                if (cType === 'select')
                {
                    this.handle.bind({
                        focus: function()
                        {
                            slider.addClass($.mobile.focusClass);
                        },

                        blur: function()
                        {
                            slider.removeClass($.mobile.focusClass);
                        }
                    });
                }

                this.handle.bind({
                    // NOTE force focus on handle
                    vmousedown: function()
                    {
                        $(this).focus();
                    },

                    vclick: false,

                    keydown: function(event)
                    {
                        var index = val();

                        if (self.options.disabled)
                        {
                            return;
                        }

                        // In all cases prevent the default and mark the handle as active
                        switch (event.keyCode)
                        {
                            case $.mobile.keyCode.HOME:
                            case $.mobile.keyCode.END:
                            case $.mobile.keyCode.PAGE_UP:
                            case $.mobile.keyCode.PAGE_DOWN:
                            case $.mobile.keyCode.UP:
                            case $.mobile.keyCode.RIGHT:
                            case $.mobile.keyCode.DOWN:
                            case $.mobile.keyCode.LEFT:
                                event.preventDefault();

                                if (!self._keySliding)
                                {
                                    self._keySliding = true;
                                    $(this).addClass("ui-state-active");
                                }
                                break;
                        }

                        // move the slider according to the keypress
                        switch (event.keyCode)
                        {
                            case $.mobile.keyCode.HOME:
                                self.refresh(min);
                                break;
                            case $.mobile.keyCode.END:
                                self.refresh(max);
                                break;
                            case $.mobile.keyCode.PAGE_UP:
                            case $.mobile.keyCode.UP:
                            case $.mobile.keyCode.RIGHT:
                                self.refresh(index + step);
                                break;
                            case $.mobile.keyCode.PAGE_DOWN:
                            case $.mobile.keyCode.DOWN:
                            case $.mobile.keyCode.LEFT:
                                self.refresh(index - step);
                                break;
                        }
                    }, // remove active mark

                    keyup: function(event)
                    {
                        if (self._keySliding)
                        {
                            self._keySliding = false;
                            $(this).removeClass("ui-state-active");
                        }
                    }
                });

                this.refresh(undefined, undefined, true);
            },

            refresh: function(val, isfromControl, preventInputUpdate)
            {

                if (this.options.disabled || this.element.attr('disabled'))
                {
                    this.disable();
                }

                var control = this.element, percent,
			cType = control[0].nodeName.toLowerCase(),
			min = cType === "input" ? parseFloat(control.attr("min")) : 0,
			max = cType === "input" ? parseFloat(control.attr("max")) : control.find("option").length - 1,
			step = (cType === "input" && parseFloat(control.attr("step")) > 0) ? parseFloat(control.attr("step")) : 1;

                if (typeof val === "object")
                {
                    var data = val,
                    // a slight tolerance helped get to the ends of the slider
				tol = 8;
                    if (!this.dragging ||
					data.pageX < this.slider.offset().left - tol ||
					data.pageX > this.slider.offset().left + this.slider.width() + tol)
                    {
                        return;
                    }
                    percent = Math.round(((data.pageX - this.slider.offset().left) / this.slider.width()) * 100);
                } else
                {
                    if (val == null)
                    {
                        val = cType === "input" ? parseFloat(control.val() || 0) : control[0].selectedIndex;
                    }
                    percent = (parseFloat(val) - min) / (max - min) * 100;
                }

                if (isNaN(percent))
                {
                    return;
                }

                if (percent < 0)
                {
                    percent = 0;
                }

                if (percent > 100)
                {
                    percent = 100;
                }

                var newval = (percent / 100) * (max - min) + min;

                //from jQuery UI slider, the following source will round to the nearest step
                var valModStep = (newval - min) % step;
                var alignValue = newval - valModStep;

                if (Math.abs(valModStep) * 2 >= step)
                {
                    alignValue += (valModStep > 0) ? step : (-step);
                }
                // Since JavaScript has problems with large floats, round
                // the final value to 5 digits after the decimal point (see jQueryUI: #4124)
                newval = parseFloat(alignValue.toFixed(5));

                if (newval < min)
                {
                    newval = min;
                }

                if (newval > max)
                {
                    newval = max;
                }

                this.handle.css("left", percent + "%");
                this.handle.attr({
                    "aria-valuenow": cType === "input" ? newval : control.find("option").eq(newval).attr("value"),
                    "aria-valuetext": cType === "input" ? newval : control.find("option").eq(newval).getEncodedText(),
                    title: cType === "input" ? newval : control.find("option").eq(newval).getEncodedText()
                });

                if (this.valuebg)
                {
                    this.valuebg.css("width", percent + "%");
                }

                // drag the label widths
                if (this._labels)
                {
                    var handlePercent = this.handle.width() / this.slider.width() * 100,
				aPercent = percent && handlePercent + (100 - handlePercent) * percent / 100,
				bPercent = percent === 100 ? 0 : Math.min(handlePercent + 100 - aPercent, 100);

                    this._labels.each(function()
                    {
                        var ab = $(this).is(".ui-slider-label-a");
                        $(this).width((ab ? aPercent : bPercent) + "%");
                    });
                }

                if (!preventInputUpdate)
                {
                    var valueChanged = false;

                    // update control"s value
                    if (cType === "input")
                    {
                        valueChanged = control.val() !== newval;
                        control.val(newval);
                    } else
                    {
                        valueChanged = control[0].selectedIndex !== newval;
                        control[0].selectedIndex = newval;
                    }
                    if (!isfromControl && valueChanged)
                    {
                        control.trigger("change");
                    }
                }
            },

            enable: function()
            {
                this.element.attr("disabled", false);
                this.slider.removeClass("ui-disabled").attr("aria-disabled", false);
                return this._setOption("disabled", false);
            },

            disable: function()
            {
                this.element.attr("disabled", true);
                this.slider.addClass("ui-disabled").attr("aria-disabled", true);
                return this._setOption("disabled", true);
            }

        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.slider.prototype.enhanceWithin(e.target, true);
        });

    })(jQuery);

    (function($, undefined)
    {

        $.widget("mobile.selectmenu", $.mobile.widget, {
            options: {
                theme: null,
                disabled: false,
                icon: "arrow-d",
                iconpos: "right",
                inline: false,
                corners: true,
                shadow: true,
                iconshadow: true,
                overlayTheme: "a",
                hidePlaceholderMenuItems: true,
                closeText: "Close",
                nativeMenu: true,
                // This option defaults to true on iOS devices.
                preventFocusZoom: /iPhone|iPad|iPod/.test(navigator.platform) && navigator.userAgent.indexOf("AppleWebKit") > -1,
                initSelector: "select:not(:jqmData(role='slider'))",
                mini: false
            },

            _button: function()
            {
                return $("<div/>");
            },

            _setDisabled: function(value)
            {
                this.element.attr("disabled", value);
                this.button.attr("aria-disabled", value);
                return this._setOption("disabled", value);
            },

            _focusButton: function()
            {
                var self = this;

                setTimeout(function()
                {
                    self.button.focus();
                }, 40);
            },

            _selectOptions: function()
            {
                return this.select.find("option");
            },

            // setup items that are generally necessary for select menu extension
            _preExtension: function()
            {
                var classes = "";
                // TODO: Post 1.1--once we have time to test thoroughly--any classes manually applied to the original element should be carried over to the enhanced element, with an `-enhanced` suffix. See https://github.com/jquery/jquery-mobile/issues/3577
                /* if( $el[0].className.length ) {
                classes = $el[0].className;
                } */
                if (!! ~this.element[0].className.indexOf("ui-btn-left"))
                {
                    classes = " ui-btn-left";
                }

                if (!! ~this.element[0].className.indexOf("ui-btn-right"))
                {
                    classes = " ui-btn-right";
                }

                this.select = this.element.wrap("<div class='ui-select" + classes + "'>");
                this.selectID = this.select.attr("id");
                this.label = $("label[for='" + this.selectID + "']").addClass("ui-select");
                this.isMultiple = this.select[0].multiple;
                if (!this.options.theme)
                {
                    this.options.theme = $.mobile.getInheritedTheme(this.select, "c");
                }
            },

            _create: function()
            {
                this._preExtension();

                // Allows for extension of the native select for custom selects and other plugins
                // see select.custom for example extension
                // TODO explore plugin registration
                this._trigger("beforeCreate");

                this.button = this._button();

                var self = this,

			options = this.options,

			inline = options.inline || this.select.jqmData("inline"),
			mini = options.mini || this.select.jqmData("mini"),
			iconpos = options.icon ? (options.iconpos || this.select.jqmData("iconpos")) : false,

                // IE throws an exception at options.item() function when
                // there is no selected item
                // select first in this case
			selectedIndex = this.select[0].selectedIndex === -1 ? 0 : this.select[0].selectedIndex,

                // TODO values buttonId and menuId are undefined here
			button = this.button
				.insertBefore(this.select)
				.buttonMarkup({
				    theme: options.theme,
				    icon: options.icon,
				    iconpos: iconpos,
				    inline: inline,
				    corners: options.corners,
				    shadow: options.shadow,
				    iconshadow: options.iconshadow,
				    mini: mini
				});

                this.setButtonText();

                // Opera does not properly support opacity on select elements
                // In Mini, it hides the element, but not its text
                // On the desktop,it seems to do the opposite
                // for these reasons, using the nativeMenu option results in a full native select in Opera
                if (options.nativeMenu && window.opera && window.opera.version)
                {
                    button.addClass("ui-select-nativeonly");
                }

                // Add counter for multi selects
                if (this.isMultiple)
                {
                    this.buttonCount = $("<span>")
				.addClass("ui-li-count ui-btn-up-c ui-btn-corner-all")
				.hide()
				.appendTo(button.addClass('ui-li-has-count'));
                }

                // Disable if specified
                if (options.disabled || this.element.attr('disabled'))
                {
                    this.disable();
                }

                // Events on native select
                this.select.change(function()
                {
                    self.refresh();
                });

                this.build();
            },

            build: function()
            {
                var self = this;

                this.select
			.appendTo(self.button)
			.bind("vmousedown", function()
			{
			    // Add active class to button
			    self.button.addClass($.mobile.activeBtnClass);
			})
			.bind("focus", function()
			{
			    self.button.addClass($.mobile.focusClass);
			})
			.bind("blur", function()
			{
			    self.button.removeClass($.mobile.focusClass);
			})
			.bind("focus vmouseover", function()
			{
			    self.button.trigger("vmouseover");
			})
			.bind("vmousemove", function()
			{
			    // Remove active class on scroll/touchmove
			    self.button.removeClass($.mobile.activeBtnClass);
			})
			.bind("change blur vmouseout", function()
			{
			    self.button.trigger("vmouseout")
					.removeClass($.mobile.activeBtnClass);
			})
			.bind("change blur", function()
			{
			    self.button.removeClass("ui-btn-down-" + self.options.theme);
			});

                // In many situations, iOS will zoom into the select upon tap, this prevents that from happening
                self.button.bind("vmousedown", function()
                {
                    if (self.options.preventFocusZoom)
                    {
                        $.mobile.zoom.disable(true);
                    }
                }).bind("mouseup", function()
                {
                    if (self.options.preventFocusZoom)
                    {
                        $.mobile.zoom.enable(true);
                    }
                });
            },

            selected: function()
            {
                return this._selectOptions().filter(":selected");
            },

            selectedIndices: function()
            {
                var self = this;

                return this.selected().map(function()
                {
                    return self._selectOptions().index(this);
                }).get();
            },

            setButtonText: function()
            {
                var self = this,
			selected = this.selected(),
			text = this.placeholder,
			span = $(document.createElement("span"));

                this.button.find(".ui-btn-text").html(function()
                {
                    if (selected.length)
                    {
                        text = selected.map(function()
                        {
                            return $(this).text();
                        }).get().join(", ");
                    } else
                    {
                        text = self.placeholder;
                    }

                    // TODO possibly aggregate multiple select option classes
                    return span.text(text)
				.addClass(self.select.attr("class"))
				.addClass(selected.attr("class"));
                });
            },

            setButtonCount: function()
            {
                var selected = this.selected();

                // multiple count inside button
                if (this.isMultiple)
                {
                    this.buttonCount[selected.length > 1 ? "show" : "hide"]().text(selected.length);
                }
            },

            refresh: function()
            {
                this.setButtonText();
                this.setButtonCount();
            },

            // open and close preserved in native selects
            // to simplify users code when looping over selects
            open: $.noop,
            close: $.noop,

            disable: function()
            {
                this._setDisabled(true);
                this.button.addClass("ui-disabled");
            },

            enable: function()
            {
                this._setDisabled(false);
                this.button.removeClass("ui-disabled");
            }
        });

        //auto self-init widgets
        $(document).bind("pagecreate create", function(e)
        {
            $.mobile.selectmenu.prototype.enhanceWithin(e.target, true);
        });
    })(jQuery);

    /*
    * custom "selectmenu" plugin
    */

    (function($, undefined)
    {
        var extendSelect = function(widget)
        {

            var select = widget.select,
			selectID = widget.selectID,
			label = widget.label,
			thisPage = widget.select.closest(".ui-page"),
			selectOptions = widget._selectOptions(),
			isMultiple = widget.isMultiple = widget.select[0].multiple,
			buttonId = selectID + "-button",
			menuId = selectID + "-menu",
			menuPage = $("<div data-" + $.mobile.ns + "role='dialog' data-" + $.mobile.ns + "theme='" + widget.options.theme + "' data-" + $.mobile.ns + "overlay-theme='" + widget.options.overlayTheme + "'>" +
				"<div data-" + $.mobile.ns + "role='header'>" +
				"<div class='ui-title'>" + label.getEncodedText() + "</div>" +
				"</div>" +
				"<div data-" + $.mobile.ns + "role='content'></div>" +
				"</div>"),

			listbox = $("<div>", { "class": "ui-selectmenu" }).insertAfter(widget.select).popup({ theme: "a" }),

			list = $("<ul>", {
			    "class": "ui-selectmenu-list",
			    "id": menuId,
			    "role": "listbox",
			    "aria-labelledby": buttonId
			}).attr("data-" + $.mobile.ns + "theme", widget.options.theme).appendTo(listbox),

			header = $("<div>", {
			    "class": "ui-header ui-bar-" + widget.options.theme
			}).prependTo(listbox),

			headerTitle = $("<h1>", {
			    "class": "ui-title"
			}).appendTo(header),

			menuPageContent,
			menuPageClose,
			headerClose;

            if (widget.isMultiple)
            {
                headerClose = $("<a>", {
                    "text": widget.options.closeText,
                    "href": "#",
                    "class": "ui-btn-left"
                }).attr("data-" + $.mobile.ns + "iconpos", "notext").attr("data-" + $.mobile.ns + "icon", "delete").appendTo(header).buttonMarkup();
            }

            $.extend(widget, {
                select: widget.select,
                selectID: selectID,
                buttonId: buttonId,
                menuId: menuId,
                thisPage: thisPage,
                menuPage: menuPage,
                label: label,
                selectOptions: selectOptions,
                isMultiple: isMultiple,
                theme: widget.options.theme,
                listbox: listbox,
                list: list,
                header: header,
                headerTitle: headerTitle,
                headerClose: headerClose,
                menuPageContent: menuPageContent,
                menuPageClose: menuPageClose,
                placeholder: "",

                build: function()
                {
                    var self = this;

                    // Create list from select, update state
                    self.refresh();

                    self.select.attr("tabindex", "-1").focus(function()
                    {
                        $(this).blur();
                        self.button.focus();
                    });

                    // Button events
                    self.button.bind("vclick keydown", function(event)
                    {
                        if (event.type === "vclick" ||
							event.keyCode && (event.keyCode === $.mobile.keyCode.ENTER ||
																event.keyCode === $.mobile.keyCode.SPACE))
                        {

                            self.open();
                            event.preventDefault();
                        }
                    });

                    // Events for list items
                    self.list.attr("role", "listbox")
					.bind("focusin", function(e)
					{
					    $(e.target)
							.attr("tabindex", "0")
							.trigger("vmouseover");

					})
					.bind("focusout", function(e)
					{
					    $(e.target)
							.attr("tabindex", "-1")
							.trigger("vmouseout");
					})
					.delegate("li:not(.ui-disabled, .ui-li-divider)", "click", function(event)
					{

					    // index of option tag to be selected
					    var oldIndex = self.select[0].selectedIndex,
							newIndex = self.list.find("li:not(.ui-li-divider)").index(this),
							option = self._selectOptions().eq(newIndex)[0];

					    // toggle selected status on the tag for multi selects
					    option.selected = self.isMultiple ? !option.selected : true;

					    // toggle checkbox class for multiple selects
					    if (self.isMultiple)
					    {
					        $(this).find(".ui-icon")
								.toggleClass("ui-icon-checkbox-on", option.selected)
								.toggleClass("ui-icon-checkbox-off", !option.selected);
					    }

					    // trigger change if value changed
					    if (self.isMultiple || oldIndex !== newIndex)
					    {
					        self.select.trigger("change");
					    }

					    // hide custom select for single selects only - otherwise focus clicked item
					    // We need to grab the clicked item the hard way, because the list may have been rebuilt
					    if (self.isMultiple)
					    {
					        self.list.find("li:not(.ui-li-divider)").eq(newIndex)
								.addClass("ui-btn-down-" + widget.options.theme).find("a").first().focus();
					    }
					    else
					    {
					        self.close();
					    }

					    event.preventDefault();
					})
					.keydown(function(event)
					{  //keyboard events for menu items
					    var target = $(event.target),
							li = target.closest("li"),
							prev, next;

					    // switch logic based on which key was pressed
					    switch (event.keyCode)
					    {
					        // up or left arrow keys 
					        case 38:
					            prev = li.prev().not(".ui-selectmenu-placeholder");

					            if (prev.is(".ui-li-divider"))
					            {
					                prev = prev.prev();
					            }

					            // if there's a previous option, focus it
					            if (prev.length)
					            {
					                target
									.blur()
									.attr("tabindex", "-1");

					                prev.addClass("ui-btn-down-" + widget.options.theme).find("a").first().focus();
					            }

					            return false;
					            // down or right arrow keys
					        case 40:
					            next = li.next();

					            if (next.is(".ui-li-divider"))
					            {
					                next = next.next();
					            }

					            // if there's a next option, focus it
					            if (next.length)
					            {
					                target
									.blur()
									.attr("tabindex", "-1");

					                next.addClass("ui-btn-down-" + widget.options.theme).find("a").first().focus();
					            }

					            return false;
					            // If enter or space is pressed, trigger click
					        case 13:
					        case 32:
					            target.trigger("click");

					            return false;
					    }
					});

                    // button refocus ensures proper height calculation
                    // by removing the inline style and ensuring page inclusion
                    self.menuPage.bind("pagehide", function()
                    {
                        self.list.appendTo(self.listbox);
                        self._focusButton();

                        // TODO centralize page removal binding / handling in the page plugin.
                        // Suggestion from @jblas to do refcounting
                        //
                        // TODO extremely confusing dependency on the open method where the pagehide.remove
                        // bindings are stripped to prevent the parent page from disappearing. The way
                        // we're keeping pages in the DOM right now sucks
                        //
                        // rebind the page remove that was unbound in the open function
                        // to allow for the parent page removal from actions other than the use
                        // of a dialog sized custom select
                        //
                        // doing this here provides for the back button on the custom select dialog
                        $.mobile._bindPageRemove.call(self.thisPage);
                    });

                    // Events on the popup
                    self.listbox.bind("closed", function(event)
                    {
                        self.close();
                    });

                    // Close button on small overlays
                    if (self.isMultiple)
                    {
                        self.headerClose.click(function()
                        {
                            if (self.menuType === "overlay")
                            {
                                self.close();
                                return false;
                            }
                        });
                    }

                    // track this dependency so that when the parent page
                    // is removed on pagehide it will also remove the menupage
                    self.thisPage.addDependents(this.menuPage);
                },

                _isRebuildRequired: function()
                {
                    var list = this.list.find("li"),
					options = this._selectOptions();

                    // TODO exceedingly naive method to determine difference
                    // ignores value changes etc in favor of a forcedRebuild
                    // from the user in the refresh method
                    return options.text() !== list.text();
                },

                selected: function()
                {
                    return this._selectOptions().filter(":selected:not(:jqmData(placeholder='true'))");
                },

                refresh: function(forceRebuild, foo)
                {
                    var self = this,
				select = this.element,
				isMultiple = this.isMultiple,
				indicies;

                    if (forceRebuild || this._isRebuildRequired())
                    {
                        self._buildList();
                    }

                    indicies = this.selectedIndices();

                    self.setButtonText();
                    self.setButtonCount();

                    self.list.find("li:not(.ui-li-divider)")
					.removeClass($.mobile.activeBtnClass)
					.attr("aria-selected", false)
					.each(function(i)
					{

					    if ($.inArray(i, indicies) > -1)
					    {
					        var item = $(this);

					        // Aria selected attr
					        item.attr("aria-selected", true);

					        // Multiple selects: add the "on" checkbox state to the icon
					        if (self.isMultiple)
					        {
					            item.find(".ui-icon").removeClass("ui-icon-checkbox-off").addClass("ui-icon-checkbox-on");
					        } else
					        {
					            if (item.is(".ui-selectmenu-placeholder"))
					            {
					                item.next().addClass($.mobile.activeBtnClass);
					            } else
					            {
					                item.addClass($.mobile.activeBtnClass);
					            }
					        }
					    }
					});
                },

                close: function()
                {
                    if (this.options.disabled || !this.isOpen)
                    {
                        return;
                    }

                    var self = this;

                    if (self.menuType === "page")
                    {
                        // doesn't solve the possible issue with calling change page
                        // where the objects don't define data urls which prevents dialog key
                        // stripping - changePage has incoming refactor
                        window.history.back();
                    } else
                    {
                        self.listbox.popup("close");
                        self.list.appendTo(self.listbox);
                        self._focusButton();
                    }

                    // allow the dialog to be closed again
                    self.isOpen = false;
                },

                open: function()
                {
                    if (this.options.disabled)
                    {
                        return;
                    }

                    var self = this,
					$window = $(window),
					selfListParent = self.list.parent(),
					menuHeight = selfListParent.outerHeight(),
					menuWidth = selfListParent.outerWidth(),
					activePage = $("." + $.mobile.activePageClass),
					scrollTop = $window.scrollTop(),
					btnOffset = self.button.offset().top,
					screenHeight = $window.height(),
					screenWidth = $window.width();

                    //add active class to button
                    self.button.addClass($.mobile.activeBtnClass);

                    //remove after delay
                    setTimeout(function()
                    {
                        self.button.removeClass($.mobile.activeBtnClass);
                    }, 300);

                    function focusMenuItem()
                    {
                        var selector = self.list.find("." + $.mobile.activeBtnClass + " a");
                        if (selector.length === 0)
                        {
                            selector = self.list.find("li.ui-btn:not(:jqmData(placeholder='true')) a");
                        }
                        selector.first().focus().closest("li").addClass("ui-btn-down-" + widget.options.theme);
                    }

                    if (menuHeight > screenHeight - 80 || !$.support.scrollTop)
                    {

                        self.menuPage.appendTo($.mobile.pageContainer).page();
                        self.menuPageContent = menuPage.find(".ui-content");
                        self.menuPageClose = menuPage.find(".ui-header a");

                        // prevent the parent page from being removed from the DOM,
                        // otherwise the results of selecting a list item in the dialog
                        // fall into a black hole
                        self.thisPage.unbind("pagehide.remove");

                        //for WebOS/Opera Mini (set lastscroll using button offset)
                        if (scrollTop === 0 && btnOffset > screenHeight)
                        {
                            self.thisPage.one("pagehide", function()
                            {
                                $(this).jqmData("lastScroll", btnOffset);
                            });
                        }

                        self.menuPage.one("pageshow", function()
                        {
                            focusMenuItem();
                            self.isOpen = true;
                        });

                        self.menuType = "page";
                        self.menuPageContent.append(self.list);
                        self.menuPage.find("div .ui-title").text(self.label.text());
                        $.mobile.changePage(self.menuPage, {
                            transition: $.mobile.defaultDialogTransition
                        });
                    } else
                    {
                        self.menuType = "overlay";

                        self.listbox
						.one("opened", focusMenuItem)
						.popup("open",
							self.button.offset().left + self.button.outerWidth() / 2,
							self.button.offset().top + self.button.outerHeight() / 2);

                        // duplicate with value set in page show for dialog sized selects
                        self.isOpen = true;
                    }
                },

                _buildList: function()
                {
                    var self = this,
					o = this.options,
					placeholder = this.placeholder,
					needPlaceholder = true,
					optgroups = [],
					lis = [],
					dataIcon = self.isMultiple ? "checkbox-off" : "false";

                    self.list.empty().filter(".ui-listview").listview("destroy");

                    var $options = self.select.find("option"),
					numOptions = $options.length,
					select = this.select[0],
					dataPrefix = 'data-' + $.mobile.ns,
					dataIndexAttr = dataPrefix + 'option-index',
					dataIconAttr = dataPrefix + 'icon',
					dataRoleAttr = dataPrefix + 'role',
					dataPlaceholderAttr = dataPrefix + 'placeholder',
					fragment = document.createDocumentFragment(),
					isPlaceholderItem = false,
					optGroup;

                    for (var i = 0; i < numOptions; i++, isPlaceholderItem = false)
                    {
                        var option = $options[i],
						$option = $(option),
						parent = option.parentNode,
						text = $option.text(),
						anchor = document.createElement('a'),
						classes = [];

                        anchor.setAttribute('href', '#');
                        anchor.appendChild(document.createTextNode(text));

                        // Are we inside an optgroup?
                        if (parent !== select && parent.nodeName.toLowerCase() === "optgroup")
                        {
                            var optLabel = parent.getAttribute('label');
                            if (optLabel !== optGroup)
                            {
                                var divider = document.createElement('li');
                                divider.setAttribute(dataRoleAttr, 'list-divider');
                                divider.setAttribute('role', 'option');
                                divider.setAttribute('tabindex', '-1');
                                divider.appendChild(document.createTextNode(optLabel));
                                fragment.appendChild(divider);
                                optGroup = optLabel;
                            }
                        }

                        if (needPlaceholder && (!option.getAttribute("value") || text.length === 0 || $option.jqmData("placeholder")))
                        {
                            needPlaceholder = false;
                            isPlaceholderItem = true;

                            // If we have identified a placeholder, mark it retroactively in the select as well
                            option.setAttribute(dataPlaceholderAttr, true);
                            if (o.hidePlaceholderMenuItems)
                            {
                                classes.push("ui-selectmenu-placeholder");
                            }
                            if (!placeholder)
                            {
                                placeholder = self.placeholder = text;
                            }
                        }

                        var item = document.createElement('li');
                        if (option.disabled)
                        {
                            classes.push("ui-disabled");
                            item.setAttribute('aria-disabled', true);
                        }
                        item.setAttribute(dataIndexAttr, i);
                        item.setAttribute(dataIconAttr, dataIcon);
                        if (isPlaceholderItem)
                        {
                            item.setAttribute(dataPlaceholderAttr, true);
                        }
                        item.className = classes.join(" ");
                        item.setAttribute('role', 'option');
                        anchor.setAttribute('tabindex', '-1');
                        item.appendChild(anchor);
                        fragment.appendChild(item);
                    }

                    self.list[0].appendChild(fragment);

                    // Hide header if it's not a multiselect and there's no placeholder
                    if (!this.isMultiple && !placeholder.length)
                    {
                        this.header.hide();
                    } else
                    {
                        this.headerTitle.text(this.placeholder);
                    }

                    // Now populated, create listview
                    self.list.listview();
                },

                _button: function()
                {
                    return $("<a>", {
                        "href": "#",
                        "role": "button",
                        // TODO value is undefined at creation
                        "id": this.buttonId,
                        "aria-haspopup": "true",

                        // TODO value is undefined at creation
                        "aria-owns": this.menuId
                    });
                }
            });
        };

        // issue #3894 - core doesn't triggered events on disabled delegates
        $(document).bind("selectmenubeforecreate", function(event)
        {
            var selectmenuWidget = $(event.target).data("selectmenu");

            if (!selectmenuWidget.options.nativeMenu &&
				selectmenuWidget.element.parents(":jqmData(role='popup')").length === 0)
            {
                extendSelect(selectmenuWidget);
            }
        });
    })(jQuery);

    (function($, undefined)
    {


        $.widget("mobile.fixedtoolbar", $.mobile.widget, {
            options: {
                visibleOnPageShow: true,
                disablePageZoom: true,
                transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
                fullscreen: false,
                tapToggle: true,
                tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
                hideDuringFocus: "input, textarea, select",
                updatePagePadding: true,
                trackPersistentToolbars: true,

                // Browser detection! Weeee, here we go...
                // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
                // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
                // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
                // The following function serves to rule out some popular browsers with known fixed-positioning issues
                // This is a plugin option like any other, so feel free to improve or overwrite it
                supportBlacklist: function()
                {
                    var w = window,
					ua = navigator.userAgent,
					platform = navigator.platform,
                    // Rendering engine is Webkit, and capture major version
					wkmatch = ua.match(/AppleWebKit\/([0-9]+)/),
					wkversion = !!wkmatch && wkmatch[1],
					ffmatch = ua.match(/Fennec\/([0-9]+)/),
					ffversion = !!ffmatch && ffmatch[1],
					operammobilematch = ua.match(/Opera Mobi\/([0-9]+)/),
					omversion = !!operammobilematch && operammobilematch[1];

                    if (
                    // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
					((platform.indexOf("iPhone") > -1 || platform.indexOf("iPad") > -1 || platform.indexOf("iPod") > -1) && wkversion && wkversion < 534) ||
                    // Opera Mini
					(w.operamini && ({}).toString.call(w.operamini) === "[object OperaMini]") ||
					(operammobilematch && omversion < 7458) ||
                    //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
					(ua.indexOf("Android") > -1 && wkversion && wkversion < 533) ||
                    // Firefox Mobile before 6.0 -
					(ffversion && ffversion < 6) ||
                    // WebOS less than 3
					("palmGetResource" in window && wkversion && wkversion < 534) ||
                    // MeeGo
					(ua.indexOf("MeeGo") > -1 && ua.indexOf("NokiaBrowser/8.5.0") > -1))
                    {
                        return true;
                    }

                    return false;
                },
                initSelector: ":jqmData(position='fixed')"
            },

            _create: function()
            {

                var self = this,
				o = self.options,
				$el = self.element,
				tbtype = $el.is(":jqmData(role='header')") ? "header" : "footer",
				$page = $el.closest(".ui-page");

                // Feature detecting support for
                if (o.supportBlacklist())
                {
                    self.destroy();
                    return;
                }

                $el.addClass("ui-" + tbtype + "-fixed");

                // "fullscreen" overlay positioning
                if (o.fullscreen)
                {
                    $el.addClass("ui-" + tbtype + "-fullscreen");
                    $page.addClass("ui-page-" + tbtype + "-fullscreen");
                }
                // If not fullscreen, add class to page to set top or bottom padding
                else
                {
                    $page.addClass("ui-page-" + tbtype + "-fixed");
                }

                self._addTransitionClass();
                self._bindPageEvents();
                self._bindToggleHandlers();
            },

            _addTransitionClass: function()
            {
                var tclass = this.options.transition;

                if (tclass && tclass !== "none")
                {
                    // use appropriate slide for header or footer
                    if (tclass === "slide")
                    {
                        tclass = this.element.is(".ui-header") ? "slidedown" : "slideup";
                    }

                    this.element.addClass(tclass);
                }
            },

            _bindPageEvents: function()
            {
                var self = this,
				o = self.options,
				$el = self.element;

                //page event bindings
                // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
                // This method is meant to disable zoom while a fixed-positioned toolbar page is visible
                $el.closest(".ui-page")
				.bind("pagebeforeshow", function()
				{
				    if (o.disablePageZoom)
				    {
				        $.mobile.zoom.disable(true);
				    }
				    if (!o.visibleOnPageShow)
				    {
				        self.hide(true);
				    }
				})
				.bind("webkitAnimationStart animationstart updatelayout", function()
				{
				    var thisPage = this;
				    if (o.updatePagePadding)
				    {
				        self.updatePagePadding(thisPage);
				    }
				})
				.bind("pageshow", function()
				{
				    var thisPage = this;
				    self.updatePagePadding(thisPage);
				    if (o.updatePagePadding)
				    {
				        $(window).bind("throttledresize." + self.widgetName, function()
				        {
				            self.updatePagePadding(thisPage);
				        });
				    }
				})
				.bind("pagebeforehide", function(e, ui)
				{
				    if (o.disablePageZoom)
				    {
				        $.mobile.zoom.enable(true);
				    }
				    if (o.updatePagePadding)
				    {
				        $(window).unbind("throttledresize." + self.widgetName);
				    }

				    if (o.trackPersistentToolbars)
				    {
				        var thisFooter = $(".ui-footer-fixed:jqmData(id)", this),
							thisHeader = $(".ui-header-fixed:jqmData(id)", this),
							nextFooter = thisFooter.length && ui.nextPage && $(".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData("id") + "')", ui.nextPage),
							nextHeader = thisHeader.length && ui.nextPage && $(".ui-header-fixed:jqmData(id='" + thisHeader.jqmData("id") + "')", ui.nextPage);

				        nextFooter = nextFooter || $();

				        if (nextFooter.length || nextHeader.length)
				        {

				            nextFooter.add(nextHeader).appendTo($.mobile.pageContainer);

				            ui.nextPage.one("pageshow", function()
				            {
				                nextFooter.add(nextHeader).appendTo(this);
				            });
				        }
				    }
				});
            },

            _visible: true,

            // This will set the content element's top or bottom padding equal to the toolbar's height
            updatePagePadding: function(tbPage)
            {
                var $el = this.element,
				header = $el.is(".ui-header");

                // This behavior only applies to "fixed", not "fullscreen"
                if (this.options.fullscreen) { return; }

                tbPage = tbPage || $el.closest(".ui-page");
                $(tbPage).css("padding-" + (header ? "top" : "bottom"), $el.outerHeight());
            },

            _useTransition: function(notransition)
            {
                var $win = $(window),
				$el = this.element,
				scroll = $win.scrollTop(),
				elHeight = $el.height(),
				pHeight = $el.closest(".ui-page").height(),
				viewportHeight = $.mobile.getScreenHeight(),
				tbtype = $el.is(":jqmData(role='header')") ? "header" : "footer";

                return !notransition &&
				(this.options.transition && this.options.transition !== "none" &&
				(
					(tbtype === "header" && !this.options.fullscreen && scroll > elHeight) ||
					(tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight)
				) || this.options.fullscreen
				);
            },

            show: function(notransition)
            {
                var hideClass = "ui-fixed-hidden",
				$el = this.element;

                if (this._useTransition(notransition))
                {
                    $el
					.removeClass("out " + hideClass)
					.addClass("in");
                }
                else
                {
                    $el.removeClass(hideClass);
                }
                this._visible = true;
            },

            hide: function(notransition)
            {
                var hideClass = "ui-fixed-hidden",
				$el = this.element,
                // if it's a slide transition, our new transitions need the reverse class as well to slide outward
				outclass = "out" + (this.options.transition === "slide" ? " reverse" : "");

                if (this._useTransition(notransition))
                {
                    $el
					.addClass(outclass)
					.removeClass("in")
					.animationComplete(function()
					{
					    $el.addClass(hideClass).removeClass(outclass);
					});
                }
                else
                {
                    $el.addClass(hideClass).removeClass(outclass);
                }
                this._visible = false;
            },

            toggle: function()
            {
                this[this._visible ? "hide" : "show"]();
            },

            _bindToggleHandlers: function()
            {
                var self = this,
				o = self.options,
				$el = self.element;

                // tap toggle
                $el.closest(".ui-page")
				.bind("vclick", function(e)
				{
				    if (o.tapToggle && !$(e.target).closest(o.tapToggleBlacklist).length)
				    {
				        self.toggle();
				    }
				})
				.bind("focusin focusout", function(e)
				{
				    if (screen.width < 500 && $(e.target).is(o.hideDuringFocus) && !$(e.target).closest(".ui-header-fixed, .ui-footer-fixed").length)
				    {
				        self[(e.type === "focusin" && self._visible) ? "hide" : "show"]();
				    }
				});
            },

            destroy: function()
            {
                this.element.removeClass("ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden");
                this.element.closest(".ui-page").removeClass("ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen");
            }

        });

        //auto self-init widgets
        $(document)
		.bind("pagecreate create", function(e)
		{

		    // DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
		    // This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
		    if ($(e.target).jqmData("fullscreen"))
		    {
		        $($.mobile.fixedtoolbar.prototype.options.initSelector, e.target).not(":jqmData(fullscreen)").jqmData("fullscreen", true);
		    }

		    $.mobile.fixedtoolbar.prototype.enhanceWithin(e.target);
		});

    })(jQuery);

    (function($, window)
    {

        // This fix addresses an iOS bug, so return early if the UA claims it's something else.
        if (!(/iPhone|iPad|iPod/.test(navigator.platform) && navigator.userAgent.indexOf("AppleWebKit") > -1))
        {
            return;
        }

        var zoom = $.mobile.zoom,
		evt, x, y, z, aig;

        function checkTilt(e)
        {
            evt = e.originalEvent;
            aig = evt.accelerationIncludingGravity;

            x = Math.abs(aig.x);
            y = Math.abs(aig.y);
            z = Math.abs(aig.z);

            // If portrait orientation and in one of the danger zones
            if (!window.orientation && (x > 7 || ((z > 6 && y < 8 || z < 8 && y > 6) && x > 5)))
            {
                if (zoom.enabled)
                {
                    zoom.disable();
                }
            } else if (!zoom.enabled)
            {
                zoom.enable();
            }
        }

        $(window)
		.bind("orientationchange.iosorientationfix", zoom.enable)
		.bind("devicemotion.iosorientationfix", checkTilt);

    } (jQuery, this));

    (function($, window, undefined)
    {
        var $html = $("html"),
			$head = $("head"),
			$window = $(window);

        //remove initial build class (only present on first pageshow)
        function hideRenderingClass()
        {
            $html.removeClass("ui-mobile-rendering");
        }

        // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
        $(window.document).trigger("mobileinit");

        // support conditions
        // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
        // otherwise, proceed with the enhancements
        if (!$.mobile.gradeA())
        {
            return;
        }

        // override ajaxEnabled on platforms that have known conflicts with hash history updates
        // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
        if ($.mobile.ajaxBlacklist)
        {
            $.mobile.ajaxEnabled = false;
        }

        // Add mobile, initial load "rendering" classes to docEl
        $html.addClass("ui-mobile ui-mobile-rendering");

        // This is a fallback. If anything goes wrong (JS errors, etc), or events don't fire,
        // this ensures the rendering class is removed after 5 seconds, so content is visible and accessible
        setTimeout(hideRenderingClass, 5000);

        $.extend($.mobile, {
            // find and enhance the pages in the dom and transition to the first page.
            initializePage: function()
            {
                // find present pages
                var $pages = $(":jqmData(role='page'), :jqmData(role='dialog')");

                // if no pages are found, create one with body's inner html
                if (!$pages.length)
                {
                    $pages = $("body").wrapInner("<div data-" + $.mobile.ns + "role='page'></div>").children(0);
                }

                // add dialogs, set data-url attrs
                $pages.each(function()
                {
                    var $this = $(this);

                    // unless the data url is already set set it to the pathname
                    if (!$this.jqmData("url"))
                    {
                        $this.attr("data-" + $.mobile.ns + "url", $this.attr("id") || location.pathname + location.search);
                    }
                });

                // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
                $.mobile.firstPage = $pages.first();

                // define page container
                $.mobile.pageContainer = $pages.first().parent().addClass("ui-mobile-viewport");

                // alert listeners that the pagecontainer has been determined for binding
                // to events triggered on it
                $window.trigger("pagecontainercreate");

                // cue page loading message
                $.mobile.showPageLoadingMsg();

                //remove initial build class (only present on first pageshow)
                hideRenderingClass();

                // if hashchange listening is disabled, there's no hash deeplink,
                // the hash is not valid (contains more than one # or does not start with #)
                // or there is no page with that hash, change to the first page in the DOM
                // Remember, however, that the hash can also be a path!
                if (!($.mobile.hashListeningEnabled &&
				$.mobile.path.isHashValid(location.hash) &&
				($(location.hash + ':jqmData(role="page")').length ||
					$.mobile.path.isPath(location.hash))))
                {
                    $.mobile.changePage($.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true });
                }
                // otherwise, trigger a hashchange to load a deeplink
                else
                {
                    $window.trigger("hashchange", [true]);
                }
            }
        });

        // initialize events now, after mobileinit has occurred
        $.mobile.navreadyDeferred.resolve();

        // check which scrollTop value should be used by scrolling to 1 immediately at domready
        // then check what the scroll top is. Android will report 0... others 1
        // note that this initial scroll won't hide the address bar. It's just for the check.
        $(function()
        {
            window.scrollTo(0, 1);

            // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
            // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
            // so if it's 1, use 0 from now on
            $.mobile.defaultHomeScroll = (!$.support.scrollTop || $(window).scrollTop() === 1) ? 0 : 1;


            // TODO: Implement a proper registration mechanism with dependency handling in order to not have exceptions like the one below
            //auto self-init widgets for those widgets that have a soft dependency on others
            if ($.fn.controlgroup)
            {
                $(document).bind("pagecreate create", function(e)
                {
                    $(":jqmData(role='controlgroup')", e.target)
					.jqmEnhanceable()
					.controlgroup({ excludeInvisible: false });
                });
            }

            //dom-ready inits
            if ($.mobile.autoInitializePage)
            {
                $.mobile.initializePage();
            }

            // window load event
            // hide iOS browser chrome on load
            $window.load($.mobile.silentScroll);

            if (!$.support.cssPointerEvents)
            {
                // IE and Opera don't support CSS pointer-events: none that we use to disable link-based buttons
                // by adding the 'ui-disabled' class to them. Using a JavaScript workaround for those browser.
                // https://github.com/jquery/jquery-mobile/issues/3558

                $(document).delegate(".ui-disabled", "vclick",
				function(e)
				{
				    e.preventDefault();
				    e.stopImmediatePropagation();
				}
			);
            }
        });
    } (jQuery, this));

}));